2011-01-30 39 views
20

Tôi đã viết một thử nghiệm về những gì tôi nghĩ rằng nên là một trường hợp hợp lệ cho một bế tắc. Nó xuất hiện rằng khi lock đã được mua lại bởi một thể hiện của một lớp, trường hợp đó không cần phải mua lại lock nữa ngay cả khi tôi rõ ràng cố gắng gọi một phương pháp khác nên lock một lần nữa.Khóa aqcuired và tiếp tục nỗ lực để khóa không chặn: là C# khóa lại entrant?

Đây là lớp:

internal class Tester 
{ 
    private readonly object _sync = new object(); 

    public Tester() { } 

    public void TestLock() 
    { 
     lock (_sync) 
     { 
      for (int i = 0; i < 10; i++) 
      { 
       Deadlock(i); 
      } 
     } 

    } 

    private void Deadlock(int i) 
    { 
     lock (_sync) 
     { 
      Trace.WriteLine(i + " no deadlock!"); 
     } 
    } 
} 

Output:

0 không bế tắc!
1 không có bế tắc!
2 không có bế tắc!
3 không có bế tắc!
4 không có bế tắc!
5 không có bế tắc!
6 không có bế tắc!
7 không có bế tắc!
8 không bế tắc!
9 không có bế tắc!

Tôi đã nghĩ rằng điều này sẽ gây ra bế tắc ... ai có thể làm sáng tỏ điều này không?

Trả lời

41

Khóa trong .NET là reentrant. Chỉ những vụ mua lại từ các chủ đề khác bị chặn. Khi cùng một chuỗi khóa cùng một đối tượng nhiều lần, nó chỉ đơn giản là tăng một bộ đếm và giảm nó khi được giải phóng. Khi bộ đếm được truy cập bằng không, khóa là thực sự là được phát hành để truy cập từ các chủ đề khác.

1

Trong trường hợp của bạn, bạn có khóa trong một khóa khác. Khi mã truy cập vào khóa lồng nhau trong "Deadlock", mã "lock (...)" về cơ bản bị bỏ qua vì nó đã mua nó trong "TestLock".

Nguồn tuyệt vời cho luồng: http://www.albahari.com/threading/part2.aspx.

+1

Tôi khá thoải mái với đa luồng, nhưng tôi đoán tôi chỉ chưa bao giờ nhận ra ổ khóa C# được tái nhập. Cảm ơn câu trả lời ... – Kiril

13

Lớp Monitor, Mutex và ReaderWriterLock khóa duy trì các khóa có ái lực luồng. Lớp ReaderWriterLockSlim cho phép bạn chọn, nó có một hàm tạo có giá trị LockRecursionPolicy. Sử dụng LockRecursionPolicy.NoRecursion là một tối ưu hóa, một cách khá lớn nếu khóa của bạn thực sự là chi tiết.

Lớp Semaphore là lớp đồng bộ hóa không có bất kỳ ái lực luồng nào. Mã này deadlocks đáng tin cậy:

class Tester { 
    private Semaphore sem = new Semaphore(1, 1); 
    public void TestLock() { 
     sem.WaitOne(); 
     for (int i = 0; i < 10; i++) Deadlock(i); 
     sem.Release(); 
    } 

    private void Deadlock(int i) { 
     if (!sem.WaitOne(100)) Console.WriteLine("deadlock!"); 
     else { 
      sem.Release(); 
      Console.WriteLine("No deadlock!"); 
     } 
    } 
} 

Nói chung, các lớp đồng bộ hóa affine yêu cầu hai chủ đề và hai khóa để bế tắc. Mẫu chuẩn là cho một sợi để có được khóa A và B, cho cái kia để có được B và A. Thứ tự quan trọng.

Có ít trường hợp deadlocks rõ ràng hơn xung quanh trong lập trình .NET, được gây ra bởi các khóa mà bạn không thể nhìn thấy vì chúng được xây dựng trong mã khuôn khổ .NET. Một phiên bản rất cổ điển dành cho BackgroundWorker. Bạn có thể viết mã trong chuỗi giao diện người dùng quay trên thuộc tính Bận, đợi BGW hoàn thành. Điều đó luôn luôn bế tắc khi BGW có trình xử lý sự kiện RunWorkerCompleted. Nó không thể chạy cho đến khi chuỗi giao diện người dùng không hoạt động, thuộc tính Bận của BGW sẽ không sai cho đến khi trình xử lý sự kiện kết thúc chạy.

+0

cảm ơn thông tin tuyệt vời! – Kiril

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