2008-11-19 43 views

Trả lời

123

Câu hỏi hay. Tôi có thể sai .. Hãy để tôi thử .. Revision # 2 của câu trả lời orig của tôi .. với một chút hiểu biết nhiều hơn. Cảm ơn đã khiến tôi đọc :)

khóa (obj)

  • là một CLR xây dựng mà cho (intra-đối tượng?) Đồng bộ hóa thread. Đảm bảo rằng chỉ một chuỗi có thể sở hữu khóa của đối tượng & nhập khối mã bị khóa. Chủ đề khác phải đợi cho đến khi chủ sở hữu hiện tại từ bỏ khóa bằng cách thoát khỏi khối mã. Ngoài ra, bạn nên khóa một đối tượng thành viên riêng của lớp.

Màn

  • khóa (obj) được thực hiện trong nội bộ bằng cách sử dụng Monitor. Bạn nên chọn khóa (obj) vì nó ngăn không cho bạn bị xáo trộn như quên quá trình dọn dẹp. Nó 'idiot-proof của màn hình xây dựng nếu bạn sẽ.
    Sử dụng Màn hình thường được ưu tiên hơn mutexes, vì màn hình được thiết kế riêng cho .NET Framework và do đó tận dụng tốt hơn các nguồn tài nguyên.

Sử dụng khóa hoặc màn hình rất hữu ích để ngăn chặn việc thực hiện đồng thời các chuỗi mã nhạy cảm, nhưng những cấu trúc này không cho phép một chuỗi liên lạc sự kiện này với sự kiện khác. Điều này đòi hỏi sự kiện đồng bộ hóa, là các đối tượng có một trong hai trạng thái, được báo hiệu và không được báo hiệu, có thể được sử dụng để kích hoạt và đình chỉ chuỗi. Mutex, Semaphores là các khái niệm cấp hệ điều hành. ví dụ như với một mutex tên bạn có thể đồng bộ hóa trên nhiều (quản lý) người yêu cũ

Mutex (đảm bảo rằng chỉ một thể hiện của ứng dụng của bạn đang chạy trên máy tính này.):

  • Không giống như màn hình, tuy nhiên, một mutex có thể được sử dụng để đồng bộ hóa các luồng trên các tiến trình. Khi được sử dụng để đồng bộ hóa liên tục, một mutex được gọi là có tên mutex vì nó được sử dụng trong một ứng dụng khác và do đó không thể chia sẻ bằng biến toàn cục hoặc biến tĩnh. Nó phải được đặt tên để cả hai ứng dụng có thể truy cập cùng một đối tượng mutex. Ngược lại, lớp Mutex là trình bao bọc cho cấu trúc Win32.Trong khi nó mạnh hơn một màn hình, một mutex đòi hỏi các quá trình chuyển đổi interop đắt hơn tính toán so với các yêu cầu của lớp Monitor.

Semaphores (tổn thương não của tôi).

  • Sử dụng lớp Semaphore để kiểm soát quyền truy cập vào một nhóm tài nguyên. Chủ đề nhập semaphore bằng cách gọi phương thức WaitOne, được thừa kế từ lớp WaitHandle, và phát hành semaphore bằng cách gọi phương thức Release. Số đếm trên một semaphore được giảm đi mỗi lần một chủ đề đi vào semaphore, và tăng lên khi một sợi phát hành semaphore. Khi số đếm bằng không, các yêu cầu tiếp theo sẽ chặn cho đến khi các luồng khác phát hành semaphore. Khi tất cả các chủ đề đã phát hành semaphore, số đếm là giá trị tối đa được chỉ định khi semaphore được tạo. Một chủ đề có thể nhập semaphore nhiều lần .. Lớp Semaphore không thực thi nhận dạng chuỗi trên WaitOne hoặc Release .. người lập trình có trách nhiệm không muck up. Semaphores có hai loại: semaphores địa phương và đặt tên là semaphores hệ thống. Nếu bạn tạo một đối tượng Semaphore bằng cách sử dụng một hàm tạo chấp nhận một tên, nó được liên kết với một semaphore của hệ điều hành của tên đó. Các ẩn dụ hệ thống được đặt tên được hiển thị trong toàn bộ hệ điều hành và có thể được sử dụng để đồng bộ hóa các hoạt động của các quá trình. Một semaphore địa phương chỉ tồn tại trong quá trình của bạn. Nó có thể được sử dụng bởi bất kỳ chủ đề nào trong quá trình của bạn có tham chiếu đến đối tượng Semaphore cục bộ. Mỗi đối tượng Semaphore là một semaphore địa phương riêng biệt.

THE PAGE TO READ - Thread Synchronization (C#)

+17

Bạn tuyên bố rằng 'Monitor' không cho phép giao tiếp là không chính xác; bạn vẫn có thể 'Pulse' vv với một' Monitor' –

+2

Kiểm tra một mô tả thay thế của Semaphores - http://stackoverflow.com/a/40473/968003. Hãy suy nghĩ về semaphores như bouncers tại một hộp đêm. Có một số lượng người được phép trong câu lạc bộ cùng một lúc. Nếu câu lạc bộ là đầy đủ không ai được phép vào, nhưng ngay sau khi một người để lại một người khác có thể nhập. –

13

Như đã nêu trong ECMA, và như bạn có thể quan sát từ phương pháp phản tuyên bố khóa về cơ bản là tương đương với

object obj = x; 
System.Threading.Monitor.Enter(obj); 
try { 
    … 
} 
finally { 
    System.Threading.Monitor.Exit(obj); 
} 

Từ ví dụ nêu trên chúng ta thấy rằng Màn hình có thể khóa trên các đối tượng.

Mutexe hữu ích khi bạn cần đồng bộ hóa interprocess vì chúng có thể khóa trên số nhận dạng chuỗi. Cùng một định danh chuỗi có thể được sử dụng bởi các quá trình khác nhau để có được khóa.

Semaphores giống như Mutexes trên steroid, chúng cho phép truy cập đồng thời bằng cách cung cấp số lượng truy cập đồng thời tối đa '. Một khi giới hạn đạt đến semaphore bắt đầu chặn bất kỳ truy cập thêm vào tài nguyên cho đến khi một trong những người gọi phát hành semaphore.

+4

Đường cú pháp này đã được thay đổi một chút trong C# 4 Kiểm tra http://blogs.msdn.com/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx –

27

Re "Sử dụng các lớp học đồng bộ Net khác" - một số người khác, bạn nên biết về:

Ngoài ra còn có nhiều cấu trúc khóa (chi phí thấp) trong CCR/TPL (Parallel Extensions CTP) - nhưng IIRC, chúng sẽ có sẵn trong .NET 4.0

+0

Vì vậy, nếu tôi muốn có một giao tiếp tín hiệu đơn giản (nói hoàn thành một op async) - tôi nên Monitor.Pulse? hoặc sử dụng SemaphoreSlim hoặc TaskCompletionSource? – Vivek

+0

Sử dụng TaskCompletionSource cho hoạt động không đồng bộ. Về cơ bản, dừng suy nghĩ về chủ đề và bắt đầu suy nghĩ về nhiệm vụ (đơn vị công việc). Chủ đề là một chi tiết triển khai và không liên quan. Bằng cách trả về TCS, bạn có thể trả lại kết quả, lỗi hoặc xử lý hủy và dễ dàng tương thích với các hoạt động không đồng bộ khác (chẳng hạn như async await hoặc ContinueWith). –

9

Một cảnh báo bổ sung để khóa trên bất kỳ Mutex được chia sẻ nào mà bạn đã xác định bằng ID chuỗi là nó sẽ mặc định thành mutex "Local \" và sẽ không được chia sẻ giữa các phiên trong môi trường máy chủ đầu cuối.

Làm tiền tố mã nhận dạng chuỗi của bạn bằng "Toàn cầu \" để đảm bảo quyền truy cập vào tài nguyên hệ thống được chia sẻ được kiểm soát chính xác. Tôi đã chỉ chạy vào một đống toàn bộ vấn đề đồng bộ hóa thông tin liên lạc với một dịch vụ đang chạy trong tài khoản SYSTEM trước khi tôi nhận ra điều này.

5

tôi sẽ cố gắng để tránh "khóa()", "Mutex" và "Giám sát" nếu bạn có thể ...

Kiểm tra các System.Collections.Concurrent namespace mới trong .NET 4
Nó có một số lớp sưu tập chủ đề an toàn tốt đẹp

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

ConcurrentDictionary rocks! không còn khóa thủ công nữa!

+1

Tránh khóa nhưng sử dụng Màn hình? Tại sao? – mafu

+0

@mafutrct Vì bạn cần phải tự lo cho việc đồng bộ hóa. –

+0

Ồ, bây giờ tôi hiểu rồi, bạn muốn tránh TẤT CẢ ba ý tưởng được đề cập. Có vẻ như bạn sẽ sử dụng Màn hình nhưng không sử dụng khóa/Mutex. – mafu

11

tôi đã làm CLR hỗ trợ các lớp học & cho luồng trong DotGNU và tôi có một vài suy nghĩ ...

Trừ khi bạn yêu cầu khóa quá trình chéo bạn nên luôn luôn tránh sử dụng Mutex & Semaphores. Các lớp này trong .NET là các trình bao bọc xung quanh Win32 Mutex và Semaphores và có trọng lượng khá nặng (chúng yêu cầu chuyển đổi ngữ cảnh vào Kernel vốn đắt - đặc biệt nếu khóa của bạn không bị tranh chấp).

Như những người khác được đề cập, các C# khóa tuyên bố là trình biên dịch ma thuật cho Monitor.Enter và Monitor.Exit (hiện có trong một thử/cuối cùng).

Màn hình có cơ chế tín hiệu/chờ đợi đơn giản nhưng mạnh mẽ mà Mutexes không có thông qua các phương thức Monitor.Pulse/Monitor.Wait. Tương đương Win32 sẽ là các đối tượng sự kiện thông qua CreateEvent mà thực sự cũng tồn tại trong .NET như WaitHandles. Mô hình Pulse/Wait tương tự như pthread_signal và pthread_wait của Unix nhưng nhanh hơn vì chúng có thể là các hoạt động chế độ người dùng hoàn toàn trong trường hợp không được tranh chấp.

Monitor.Pulse/Wait rất dễ sử dụng. Trong một chủ đề, chúng ta khóa một đối tượng, kiểm tra cờ/state/property và nếu nó không phải là những gì chúng ta đang mong đợi, hãy gọi Monitor.Wait để giải phóng khóa và chờ cho đến khi một xung được gửi đi. Khi chờ đợi trả về, chúng tôi lặp lại và kiểm tra lại cờ/trạng thái/thuộc tính. Trong chủ đề khác, chúng ta khóa đối tượng bất cứ khi nào chúng ta thay đổi cờ/state/property và sau đó gọi PulseAll để đánh thức bất kỳ chủ đề nghe nào.

Thông thường chúng tôi muốn các lớp của chúng tôi là chủ đề an toàn, vì vậy chúng tôi đặt khóa trong mã của chúng tôi. Tuy nhiên, thường là trường hợp lớp của chúng ta sẽ chỉ được sử dụng bởi một luồng. Điều này có nghĩa là các ổ khóa không cần thiết làm chậm mã của chúng tôi ... đây là nơi tối ưu hóa thông minh trong CLR có thể giúp cải thiện hiệu suất.

Tôi không chắc về việc triển khai khóa của Microsoft nhưng trong DotGNU và Mono, cờ trạng thái khóa được lưu trữ trong tiêu đề của mọi đối tượng. Mỗi đối tượng trong .NET (và Java) có thể trở thành một khóa để mọi đối tượng cần hỗ trợ điều này trong tiêu đề của chúng. Trong việc triển khai DotGNU, có một lá cờ cho phép bạn sử dụng một hashtable toàn cục cho mọi đối tượng được sử dụng như một khóa - điều này có lợi cho việc loại bỏ chi phí 4 byte cho mọi đối tượng. Đây không phải là tuyệt vời cho bộ nhớ (đặc biệt là cho các hệ thống nhúng mà không phải là luồng rất nhiều) nhưng có một hit về hiệu suất.

Cả Mono và DotGNU hiệu quả sử dụng mutexes để thực hiện khóa/chờ đợi nhưng sử dụng một phong cách spinlock compare-and-exchange hoạt động để loại bỏ sự cần thiết phải thực hiện một khóa cứng trừ khi thực sự cần thiết:

Bạn có thể xem ví dụ về màn hình như thế nào có thể được thực hiện ở đây:

http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup

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