2010-02-10 32 views
14

Tôi đã đọc trên .NET Threading và đang làm việc trên một số mã sử dụng một số ManualResetEvent. Tôi đã tìm thấy rất nhiều mẫu mã trên internet. Tuy nhiên, khi đọc tài liệu cho WaitHandle, tôi thấy như sau:Tôi có cần gọi Close() trên ManualResetEvent không?

WaitHandle thực hiện việc hủy bỏ mẫu. Xem Triển khai hoàn tất và Vứt bỏ để xóa các tài liệu không được quản lý .

Không ai trong số các mẫu dường như gọi .Close() trên các đối tượng ManualResetEvent họ tạo ra, ngay cả những đẹp Recursion and Concurrency bài viết từ blog của pfxteam (Sửa - điều này có một khối sử dụng tôi đã bỏ lỡ). Đây có phải là ví dụ giám sát, hoặc không cần thiết? Tôi tò mò vì một WaitHandle "đóng gói các đối tượng cụ thể của hệ điều hành," vì vậy có thể dễ dàng bị rò rỉ tài nguyên.

Trả lời

11

Nói chung, nếu một đối tượng thực hiện IDisposable thì làm như vậy vì một lý do và bạn nên gọi Dispose (hoặc Close, như trường hợp có thể). Trong ví dụ trang web của bạn, ManualResetEvent được bao bọc bên trong câu lệnh using, điều này sẽ "tự động" xử lý cuộc gọi Dispose. Trong trường hợp này, Close đồng nghĩa với Dispose (điều này đúng trong hầu hết các yêu cầu IDisposable cung cấp phương thức Close).

Mã từ ví dụ:

using (var mre = new ManualResetEvent(false)) 
{ 
    ... 
} 

mở rộng để

var mre = new ManualResetEvent(false); 
try 
{ 
    ... 
} 
finally 
{ 
    ((IDispoable)mre).Dispose(); 
} 
2

Bạn sẽ nhận thấy các mã

using (var mre = new ManualResetEvent(false)) 
{ 
    // Process the left child asynchronously 
    ThreadPool.QueueUserWorkItem(delegate 
    { 
     Process(tree.Left, action); 
     mre.Set(); 
    }); 

    // Process current node and right child synchronously 
    action(tree.Data); 
    Process(tree.Right, action); 

    // Wait for the left child 
    mre.WaitOne(); 
} 

sử dụng từ khóa 'sử dụng'. Điều này tự động gọi phương thức vứt bỏ khi hoàn thành ngay cả khi mã ném một ngoại lệ.

+0

Tôi hoàn toàn bỏ lỡ khối sử dụng khi xem qua mã đó. Cảm ơn đã chỉ ra điều đó. –

2

Tôi đã sử dụng ManualResetEvent rất nhiều và không nghĩ rằng tôi đã từng sử dụng nó bên trong một method-- đơn nó luôn luôn là một trường thể hiện của một lớp. Do đó, using() thường không áp dụng.

Nếu bạn có trường cá thể lớp là phiên bản ManualResetEvent, hãy đặt lớp của bạn triển khai IDisposable và trong phương thức Dispose() gọi ManualResetEvent.Close(). Sau đó, trong tất cả các tập quán của lớp học của bạn, bạn cần phải sử dụng using() hoặc làm cho lớp chứa thực hiện IDisposable và lặp lại, và lặp lại ...

2

Nếu bạn đang sử dụng một ManualResetEvent với phương pháp vô danh thì nó rõ ràng là hữu ích. Nhưng như Sam đã đề cập họ thường xuyên có thể được thông qua xung quanh thành công nhân, và sau đó thiết lập và đóng cửa.

Vì vậy, tôi sẽ nói điều đó phụ thuộc vào ngữ cảnh của cách bạn đang sử dụng nó - the MSDN WaitHandle.WaitAll() mẫu mã có một ví dụ hay về ý tôi.

Dưới đây là một ví dụ dựa trên mẫu MSDN về cách tạo WaitHandles với một tuyên bố using sẽ ngoại lệ:

System.ObjectDisposedException
"Safe xử lý đã bị đóng cửa"

const int threads = 25; 

void ManualWaitHandle() 
{ 
    ManualResetEvent[] manualEvents = new ManualResetEvent[threads]; 

    for (int i = 0; i < threads; i++) 
    { 
     using (ManualResetEvent manualResetEvent = new ManualResetEvent(false)) 
     { 
      ThreadPool.QueueUserWorkItem(new WaitCallback(ManualWaitHandleThread), new FileState("filename", manualResetEvent)); 
      manualEvents[i] = manualResetEvent; 
     } 
    } 

    WaitHandle.WaitAll(manualEvents); 
} 

void ManualWaitHandleThread(object state) 
{ 
    FileState filestate = (FileState) state; 
    Thread.Sleep(100); 
    filestate.ManualEvent.Set(); 
} 

class FileState 
{ 
    public string Filename { get;set; } 
    public ManualResetEvent ManualEvent { get; set; } 

    public FileState(string fileName, ManualResetEvent manualEvent) 
    { 
     Filename = fileName; 
     ManualEvent = manualEvent; 
    } 
} 
+0

Điều đó dường như là một ví dụ trong đó .Close() không được gọi trên ManualResetEvent và không có khối sử dụng. Tôi không nghĩ rằng các công nhân có thể đóng nó sau khi một bộ bởi vì các chủ đề chính là sử dụng nó trong WaitHandle.WaitAll (manualEvents) cuộc gọi. –

+0

@Kevin quan điểm của tôi là mảng WaitHandles không thể được bao bọc trong mệnh đề sử dụng khi được tạo thành * Tôi nghĩ * họ sẽ bị đóng khi họ đến, tôi cần phải kiểm tra. –

17

Gần đây tôi đã chuyển tiếp một đoạn trích từ C# 4.0 in a Nutshell: The Definitive Reference Tác giả Joseph Albahari, Ben Albahar tôi. Trên trang 834, trong Chương 21: Threading có một phần nói về điều này.

Vứt bỏ Chờ Xử lý

Khi bạn đã kết thúc với một tay cầm chờ đợi , bạn có thể gọi nó Đóng phương pháp để phát hành hệ điều hành tài nguyên. Ngoài ra, bạn có thể chỉ đơn giản là thả tất cả các tài liệu tham khảo để chờ đợi xử lý và cho phép thu gom rác để làm công việc cho bạn đôi khi sau này (chờ xử lý thực hiện các mô hình xử lý theo đó finalizer gọi Đóng). Đây là một trong số ít kịch bản mà dựa vào sao lưu này là (cho là) ​​có thể chấp nhận, bởi vì chờ xử lý có một hệ điều hành gánh nặng nhẹ (đại biểu không đồng bộ dựa vào chính xác cơ chế này để phát hành xử lý chờ đợi IAsyncResult của họ).

Xử lý chờ được giải phóng tự động khi ứng dụng tải miền.

+0

Tài liệu cho WaitHandle.Finalize cho biết không còn thực hiện từ .NET 2.0 nữa. Bạn cũng có thể thấy điều này với một trình dịch ngược. WaitHandle không còn có finalizer nữa. Tôi không biết tại sao, nhưng bất kỳ WaitHandle bị bỏ rơi nào cũng sẽ bị rò rỉ. Xem http://msdn.microsoft.com/en-us/library/vstudio/bb291974(v=vs.90).aspx – Djof

+4

@Djof: Mặc dù 'WaitHandle' không còn có phương thức' Finalize' nữa, tôi don ' t nghĩ rằng có nghĩa là họ bị rò rỉ. Thay vào đó, dọn dẹp được xử lý trong một 'SafeHandle' mà một' WaitHandle' giữ một tham chiếu. – supercat

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