blocking
có nghĩa là hoạt động như một gợi ý cho mã ExecutionContext
rằng mã bị chặn đang chặn và có thể dẫn đến sự cố đói. Điều này sẽ cung cấp cho các hồ bơi thread một cơ hội để đẻ trứng chủ đề mới để ngăn chặn nạn đói. Đây là ý nghĩa của "điều chỉnh hành vi thời gian chạy". Nó không phải là ma thuật mặc dù, và sẽ không làm việc với mọi ExecutionContext
.
Hãy xem xét ví dụ sau:
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)
}
này được sử dụng mặc định toàn cầu ExecutionContext
. Chạy mã này, bạn sẽ nhận thấy rằng tất cả 100 Future
s đều được thực hiện ngay lập tức, nhưng nếu bạn xóa blocking
, chúng chỉ thực thi một vài lần tại một thời điểm. Mặc định ExecutionContext
sẽ phản ứng để chặn các cuộc gọi (được đánh dấu như vậy) bằng cách sinh ra các luồng mới, và do đó không bị quá tải khi chạy Future
s.
Bây giờ nhìn vào ví dụ này với một hồ bơi cố định của 4 chủ đề:
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)
}
ExecutionContext
này không được xây dựng để xử lý sinh sản chủ đề mới, và vì vậy ngay cả với mã chặn tôi bao quanh với blocking
, bạn có thể nhìn thấy rằng nó sẽ vẫn chỉ thực hiện tối đa 4 Future
s tại một thời điểm. Và vì vậy đó là lý do tại sao chúng tôi nói nó "có thể cải thiện hiệu suất hoặc tránh deadlocks" - nó không được bảo đảm. Như chúng ta thấy ở sau ExecutionContext
, nó không được đảm bảo chút nào.
Tính năng này hoạt động như thế nào? Như liên kết, blocking
thực thi mã này:
BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission)
BlockContext.current
lấy BlockContext
từ thread hiện tại, nhìn thấy here. A BlockContext
thường chỉ là một Thread
với đặc tính BlockContext
được trộn lẫn. Như được thấy trong nguồn, nó được lưu trữ trong một ThreadLocal
hoặc nếu không tìm thấy ở đó, thì đó là mẫu khớp với chuỗi hiện tại. Nếu chuỗi hiện tại không phải là BlockContext
, thì thay vào đó, DefaultBlockContext
sẽ được sử dụng.
Tiếp theo, blockOn
được gọi là BlockContext
hiện tại. blockOn
là một phương pháp trừu tượng trong BlockContext
, do đó, việc triển khai phụ thuộc vào cách ExecutionContext
xử lý nó. Nếu chúng ta nhìn vào implementation for DefaultBlockContext
(khi luồng hiện tại không phải là BlockContext
), chúng ta thấy rằng blockOn
thực sự không có gì ở đó.Vì vậy, sử dụng blocking
trong một không BlockContext
có nghĩa là không có gì đặc biệt được thực hiện ở tất cả, và mã được chạy như là, không có tác dụng phụ.
Còn chủ đề là BlockContext
thì sao? Ví dụ: trong ngữ cảnh global
, hãy xem here, blockOn
hoạt động tốt hơn một chút. Đào sâu hơn, bạn có thể thấy rằng nó đang sử dụng ForkJoinPool
dưới mui xe, với DefaultThreadFactory
được xác định trong cùng một đoạn mã được sử dụng để sinh ra các chuỗi mới trong ForkJoinPool
. Nếu không triển khai blockOn
từ số BlockContext
(chuỗi), thì ForkJoinPool
không biết bạn đang chặn và sẽ không cố gắng sinh ra nhiều chuỗi hơn để phản hồi.
Scala's Await
cũng vậy, sử dụng blocking
để triển khai.
Dường như điều này hoàn toàn là ủy quyền cho phép thuật của Java ['util.concurrent.ForkJoinPool's'] (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.ManagedBlocker .html) tùy ý, ngoài một số mã scala không được chấp nhận. Tôi tự hỏi sau đó, làm thế nào để ngã ba của Java tham gia thực hiện sau đó quyết định liệu/khi để đẻ trứng một chủ đề mới. Trạng thái luồng của Java có vẻ hơi kém [không đủ] (http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.State.html#BLOCKED) làm tiêu chí, trừ khi _any chặn code_ sẽ liên quan đến khóa màn hình. – matanster
Đối với cách ForkJoinPool quyết định, bạn có thể tìm thấy câu trả lời trong [source] (http://docjar.com/html/api/java/util/concurrent/ForkJoinPool.java.html) một chút ít hơn thỏa mãn. Mọi thứ bắt đầu có mức độ rất thấp. 'addWorker' có thể tạo các luồng mới từ' ForkJoinWorkerThreadFactory', được gọi ở hai nơi: 'signalWork' và' tryPreBlock'. Cả hai đều chứa một số lượng thú vị của logic bitwise. –
Hãy để chúng tôi để một câu hỏi riêng biệt (hoặc cuốn sách) ... cho đến khi được hiển thị khác, tôi cho rằng hầu hết các api chặn có thể đủ khả năng phát hiện bởi thực hiện Java 7 đó, ngoại trừ một nhiệm vụ chuyên sâu cpu dài có thể được coi là "chặn" trong chế độ xem "Ứng dụng phản hồi" của TypeSafe. – matanster