scala./ align = "left" / blokowanie-co to właściwie robi?
Spędziłem chwilę ucząc się tematu kontekstów wykonywania Scali, bazujących na modelach wątkowych i współbieżności. Czy możesz wyjaśnić, w jaki sposób scala.concurrent.blocking
"Dostosuj zachowanie runtime" i "może poprawić wydajność lub uniknąć blokad" , jak opisano w scaladoc?
W Dokumentacja jest przedstawiona jako sposób oczekiwania na api, które nie implementuje Awaitable. (Być może również po prostu długie obliczenia powinny być zapakowane?).
Co to właściwie robi?
Podążanie za źródłem nie łatwo zdradza jego sekrety.
1 answers
blocking
ma działać jako wskazówka do ExecutionContext
, że zawarty kod blokuje się i może prowadzić do głodu wątku. To da puli wątków szansę na odradzanie nowych wątków, aby zapobiec głodowi. To jest to, co oznacza "dostosuj zachowanie środowiska wykonawczego". Nie jest to jednak magia i nie będzie działać z każdym ExecutionContext
.
Rozważ ten przykład:
import scala.concurrent._
val ec = scala.concurrent.ExecutionContext.Implicits.global
(0 to 100) foreach { n =>
Future {
println("starting Future: " + n)
blocking { Thread.sleep(3000) }
println("ending Future: " + n)
}(ec)
}
Używa się domyślnego globalnego ExecutionContext
. Uruchamiając kod tak, jak jest, zauważysz, że 100 Future
s wszystkie są wykonywane natychmiast, ale jeśli usuniesz blocking
, wykonają tylko kilka na raz. Domyślnie ExecutionContext
będzie reagować na blokowanie połączeń (oznaczone jako takie) przez wywoływanie nowych wątków, a tym samym nie będzie przeciążone uruchomieniem Future
s.
Teraz spójrz na ten przykład ze stałą pulą 4 wątków:
import java.util.concurrent.Executors
val executorService = Executors.newFixedThreadPool(4)
val ec = ExecutionContext.fromExecutorService(executorService)
(0 to 100) foreach { n =>
Future {
println("starting Future: " + n)
blocking { Thread.sleep(3000) }
println("ending Future: " + n)
}(ec)
}
Ten ExecutionContext
nie jest zbudowany do obsługi spawania nowych wątków, więc nawet z moim kodem blokującym otoczonym blocking
, widać, że nadal będzie wykonywał tylko co najwyżej 4 Future
s W czas. I dlatego mówimy, że "może poprawić wydajność lub uniknąć impasu" {47]} - to nie jest gwarantowane. Jak widzimy w tym drugim ExecutionContext
, nie jest to w ogóle gwarantowane.
blocking
wykonuje ten kod:
BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission)
BlockContext.current
pobiera BlockContext
z bieżącego wątku, widzianego tutaj . A BlockContext
jest zwykle po prostu Thread
z BlockContext
cecha zmieszana. Jak widać w źródle, jest albo przechowywany w ThreadLocal
, albo jeśli go tam nie ma, jest wzór dopasowany z bieżącego wątku. Jeśli bieżący wątek nie jest BlockContext
, to zamiast niego używany jest DefaultBlockContext
.
Następny, blockOn
jest wywoływany na bieżącym BlockContext
. blockOn
jest metodą abstrakcyjną w BlockContext
, więc jej implementacja zależy od tego, jak ExecutionContext
ją obsługuje. Jeśli przyjrzymy się implementacji dla DefaultBlockContext
(gdy bieżący wątek nie jest BlockContext
), widzimy, że blockOn
W rzeczywistości nic tam nie robi. Więc użycie blocking
w nie BlockContext
oznacza, że nic specjalnego nie robi się w wszystkie, A kod jest uruchamiany tak, jak jest, bez skutków ubocznych.
A co z wątkami, które są BlockContext
s? Na przykład w kontekście global
, widzianym tutaj, blockOn
robi coś więcej. Kopając głębiej, widać, że używa ForkJoinPool
pod maską, z DefaultThreadFactory
zdefiniowanym w tym samym fragmencie, używanym do wywoływania nowych wątków w ForkJoinPool
. Bez implementacji blockOn
z BlockContext
(wątek), ForkJoinPool
nie wie, że blokujesz i nie będzie próbował wywołać więcej wątków w odpowiedzi.
Scala ' s Await
również używa blocking
do jego implementacji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-17 17:04:26