Người đăng ký lại khóa trong C #


119

Đoạn mã sau có dẫn đến bế tắc khi sử dụng C # trên .NET không?

 class MyClass
 {
    private object lockObj = new object();

    public void Foo()
    {
        lock(lockObj)
        { 
             Bar();
        }
    }

    public void Bar()
    {
        lock(lockObj)
        { 
          // Do something 
        }
    }       
 }

6
Chúng ta có thể xem xét việc thay đổi tiêu đề của câu hỏi này - có lẽ thành một cái gì đó giống như cái gì đó gần đây đã đóng Tại sao các ổ khóa lồng nhau không gây ra bế tắc? Như hiện tại, tiêu đề dường như được thiết kế để ngăn mọi người phát hiện ra nó.
Jeff Sternal

12
Trên thực tế, tôi tìm thấy điều này dựa trên từ tìm kiếm 'reentrant' và nó đã trả lời câu hỏi của tôi. Nếu đó là một câu hỏi dup, đó là một vấn đề khác ...
emfurry

Tôi đồng ý với nhận xét của @JeffSternal câu hỏi này giả định rằng người đang tìm kiếm câu hỏi đã quen thuộc với các khóa "Người đăng ký lại". Một câu hỏi trùng lặp khác mà tôi nghĩ có một tiêu đề tốt cho điều này: stackoverflow.com/questions/3687505/…
Luis Perez

Câu trả lời:


148

Không, miễn là bạn đang khóa trên cùng một đối tượng. Mã đệ quy thực sự đã có khóa và do đó có thể tiếp tục mà không bị cản trở.

lock(object) {...}là viết tắt để sử dụng lớp Màn hình . Như Marc đã chỉ ra , Monitorcho phép re-entrancy , vì vậy các nỗ lực lặp đi lặp lại để khóa đối tượng mà luồng hiện tại đã có khóa sẽ hoạt động tốt.

Nếu bạn bắt đầu khóa các đối tượng khác nhau , đó là lúc bạn phải cẩn thận. Đặc biệt chú ý đến:

  • Luôn có được các khóa trên một số đối tượng nhất định trong cùng một chuỗi.
  • Luôn mở khóa theo trình tự ngược lại với cách bạn có được chúng.

Nếu bạn phá vỡ một trong hai quy tắc này, bạn chắc chắn sẽ gặp phải sự cố bế tắc tại một số điểm .

Đây là một trang web tốt mô tả đồng bộ hóa luồng trong .NET: http://dotnetdebug.net/2005/07/20/monitor-class-avoiding-deadlocks/

Ngoài ra, hãy khóa càng ít đối tượng cùng lúc càng tốt. Xem xét việc áp dụng các khóa hạt thô nếu có thể. Ý tưởng là nếu bạn có thể viết mã của mình sao cho có một biểu đồ đối tượng và bạn có thể có được các khóa trên gốc của biểu đồ đối tượng đó, thì hãy làm như vậy. Điều này có nghĩa là bạn có một khóa trên đối tượng gốc đó và do đó không phải lo lắng quá nhiều về trình tự mà bạn có được / phát hành khóa.

(Một lưu ý nữa, về mặt kỹ thuật ví dụ của bạn không phải là đệ quy. Đối với nó là đệ quy, Bar()sẽ phải gọi chính nó, thường là một phần của phép lặp.)


1
Đặc biệt là trong các trình tự khác nhau.
Marc Gravell

6
Đệ quy lại; thật; vì lợi ích của Guy, thuật ngữ này sẽ bắt đầu trở lại
Marc Gravell

Cảm ơn bạn đã giải thích rõ về thuật ngữ - Tôi đã chỉnh sửa và sửa câu hỏi.
Guy

Câu hỏi này dường như nhận được khá nhiều sự chú ý nên tôi đã cập nhật câu trả lời của mình với một vài ghi chú khác mà tôi đã nghĩ ra kể từ lần đầu tiên viết nó.
Neil Barnwell,

Trên thực tế, tôi không nghĩ thứ tự bạn mở khóa quan trọng. Thứ tự mà bạn chọn chúng chắc chắn có, nhưng miễn là việc nhả khóa không có điều kiện dựa trên bất kỳ điều gì (bạn có thể nhả bất cứ lúc nào), bạn sẽ ổn miễn là bạn nhả tất cả các ổ khóa mà bạn đã có.
bobroxsox

20

Chà, Monitorcho phép tái thích, vì vậy bạn không thể tự bế tắc ... vì vậy không: điều đó không nên làm


7

Nếu một luồng đã được giữ khóa, thì nó sẽ không tự chặn. Khuôn khổ .Net đảm bảo điều này. Bạn chỉ phải đảm bảo rằng hai luồng không cố gắng lấy hai khóa giống nhau ra khỏi trình tự bằng bất kỳ đường dẫn mã nào.

Cùng một chủ đề có thể yêu cầu cùng một ổ khóa nhiều lần, nhưng bạn phải đảm bảo rằng bạn mở khóa cùng số lần mà bạn lấy được nó. Tất nhiên, miễn là bạn đang sử dụng từ khóa "lock" để thực hiện điều này, nó sẽ tự động xảy ra.


Lưu ý rằng điều đó đúng với màn hình, nhưng không nhất thiết là các loại khóa khác.
Jon Skeet

(Không có nhu cầu ngụ ý rằng bạn không biết rằng, tất nhiên - chỉ rằng đó là một sự khác biệt quan trọng :)
Jon Skeet

Đó là một điểm hay. Tôi đã thực sự định thay đổi "khóa" thành "giám sát" trong suốt, nhưng sau đó tôi bị phân tâm. Và lười biếng. Và hành vi cũng đúng với các đối tượng mutex kernal của Windows, vì vậy tôi đã tìm ra, đủ gần!
Jeffrey L Whitledge

5

Không, mã này sẽ không có khóa chết. Nếu bạn thực sự muốn tạo deadlock đơn giản nhất, bạn cần ít nhất 2 tài nguyên. Cân nhắc kịch bản về chó và xương. 1. Một con chó có toàn quyền đối với 1 khúc xương để bất kỳ con chó nào khác phải chờ đợi. 2. Tối thiểu phải có 2 con chó với 2 chiếc xương để tạo ra bế tắc khi chúng khóa xương tương ứng và tìm kiếm xương của người khác.

.. vv và như vậy n con chó và m xương và gây ra những bế tắc tinh vi hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.