2012-01-19 45 views
5

Tôi đang làm việc trong đa luồng trong ruby. Đoạn mã làBế tắc trong Ruby tham gia()

threads_array = Array.new(num_of_threads) 
    1.upto(num_of_threads) do |i| 

    Thread.abort_on_exception = true 
     threads_array[i-1] = Thread.new { 
     catch(:exit) do 
      print "s #{i}" 
      user_id = nil 
      loop do 
      user_id = user_ids.pop() 
      if user_id == nil 
       print "a #{i}" 
       Thread.stop() 
      end 
      dosomething(user_id) 
      end 
     end 
     } 
    end 
    #puts "after thread" 
    threads_array.each {|thread| thread.join} 

Tôi không sử dụng bất kỳ khóa mutex nào. Nhưng tôi nhận được bế tắc .. Sau đây là đầu ra của đoạn mã trên ..

s 2s 6s 8s 1s 11s 7s 10s 14s 16s 21s 24s 5s 26s 3s 19s 20s 23s 4s 28s 9s 12s 18s 22s 29s 30s 27s 13s 17s 15s 25a 4a 10a 3a 6a 21a 24a 16a 9a 18a 5a 28a 20a 2a 22a 11a 29a 8a 14a 23a 26a 1a 19a 7a 12fatal: bế tắc phát hiện

Kết quả trên cho chúng ta bế tắc đó là sau khi mảng user_ids là null và xảy ra với join() và stop() của lớp Thread trong ruby ​​.. Điều gì thực sự đang xảy ra và giải pháp cho lỗi này là gì ??

+0

câu trả lời của tôi có hữu ích không? Bạn đã giải quyết được vấn đề chưa? –

Trả lời

19

Các simples mã để tạo lại vấn đề này là:

t = Thread.new { Thread.stop } 
t.join # => exception in `join': deadlock detected (fatal) 

Chủ đề :: dừng → nil

Dừng thực hiện các đề hiện tại, đưa nó vào một “giấc ngủ” nhà nước và thực hiện lịch trình của một chuỗi khác.

Chủ đề # tham gia → Thr
Chủ đề # tham gia (giới hạn) → Thr

Các thread gọi sẽ đình chỉ thi công và chạy Thr. Không trả lại cho đến khi thoát số một hoặc cho đến khi giới hạn giây trôi qua. Nếu giới hạn thời gian hết hạn, số không được trả lại, nếu không thì số tiền được trả lại.

Theo như tôi hiểu bạn gọi Thread.join mà không cần tham số trên chủ đề và chờ cho nó để thoát ra, nhưng các chủ đề con gọi Thread.stop và đi vào sleep trạng thái. Đây là một tình huống deadloc - chủ đề chính chờ cho thread con thoát ra, nhưng thread con đang ngủ và không trả lời.

Nếu bạn gọi join với limit tham số sau đó sợi con sẽ được hủy bỏ sau thời gian chờ mà không gây bế tắc với chương trình của bạn:

t = Thread.new { Thread.stop } 
t.join 1 # => Process finished with exit code 0 

Tôi muốn giới thiệu để thoát đề người lao động của mình sau khi họ đã làm việc với Thread.exit hoặc nhận loại bỏ vòng lặp vô hạn và đạt đến cuối chuỗi thực hiện bình thường, ví dụ:

if user_id == nil 
    raise StopIteration 
end 

#or 
if user_id == nil 
    Thread.exit 
end 
+0

Viết tốt; làm tốt. – Phrogz

+0

tuyệt vời, cảm ơn – glebm

0

Nếu tôi có ý định của bạn, tôi sẽ xem xét điều gì đó đơn giản hơn (và có thể an toàn hơn, users_ids.pop() từ bên trong chủ đề trông đáng sợ với tôi):

user_ids = (0..19).to_a 
number_of_threads = 3 

user_ids \ 
    .each_slice(user_ids.length/number_of_threads + 1) \ 
    .map { |slice| 
     Thread.new(slice) { |s| 
     puts s.inspect 
     } 
    }.map(&:join) 
5

Ngoài câu trả lời Alex Kliuchnikau, tôi sẽ thêm rằng #join có thể nâng cao được lỗi này khi chủ đề đang chờ Queue#pop. Một giải pháp đơn giản và có ý thức là gọi #join với thời gian chờ.

Đây là từ ruby ​​2.2.2:

[27] pry(main)> q=Queue.new 
=> #<Thread::Queue:0x00000003a39848> 
[30] pry(main)> q << "asdggg" 
=> #<Thread::Queue:0x00000003a39848> 
[31] pry(main)> q << "as" 
=> #<Thread::Queue:0x00000003a39848> 
[32] pry(main)> t = Thread.new { 
[32] pry(main)* while s = q.pop 
[32] pry(main)*  puts s 
[32] pry(main)* end 
[32] pry(main)* } 
asdggg 
as 
=> #<Thread:[email protected](pry):34 sleep> 
[33] pry(main)> q << "asg" 
asg 
=> #<Thread::Queue:0x00000003a39848> 
[34] pry(main)> q << "ashg" 
ashg 
=> #<Thread::Queue:0x00000003a39848> 
[35] pry(main)> t.join 
fatal: No live threads left. Deadlock? 
from (pry):41:in `join' 
[36] pry(main)> t.join(5) 
=> nil 
+0

nếu hàng đợi là kết nối liên tục http thì sao? chẳng hạn như luồng trực tiếp và kết quả trong luồng là ngẫu nhiên, t.join (5) vẫn hoạt động? –

+0

@crazy_phage, tôi chưa có trường hợp sử dụng này nhưng tôi không thấy lý do tại sao nó không hoạt động. Trong trường hợp kết nối HTTP liên tục, tôi đoán bạn đang triển khai thời gian chờ sau khi kết nối phải được đóng, đúng không? Nếu bạn muốn chờ đợi mãi mãi, bạn có thể đặt một giá trị rất lớn, như 10 năm. – akostadinov

+0

cũng giống như kết nối http là một đường ống và tôi có một chuỗi khác để đọc từ đường ống, nếu tôi sử dụng t.join không phải j.join 5, nó sẽ bị lỗi, nhưng tôi không thấy tại sao điều đó xảy ra, coz I chạy trong sidekiq, nhật ký không hiển thị bất cứ điều gì. Vì vậy, tôi chỉ nhìn thấy câu trả lời của bạn, và tôi đoán đây là lý do tại sao điều này xảy ra. –

Các vấn đề liên quan