2010-06-21 12 views
8

Có cách nào tiêu chuẩn để đóng ứng dụng "sạch" trong khi một số đối tượng WaitHandle có thể đang ở trạng thái cuộc gọi chặn hiện tại tới WaitOne không?Làm thế nào để bạn đóng một ứng dụng khi một số WaitHandle đang ở giữa một cuộc gọi đến WaitOne?

Ví dụ, có thể có một sợi nền được quay cùng một phương pháp như thế này:

while (_request.WaitOne()) 
{ 
    try 
    { 
     _workItem.Invoke(); 
    } 
    finally 
    { 
     OnWorkCompleted(); 
    } 
} 

tôi thấy không có cách nào rõ ràng để định đoạt của chủ đề này mà không gọi Thread.Abort (mà từ những gì tôi hiểu là nản lòng). Tuy nhiên, hãy gọi số Close đối tượng _request (số AutoResetEvent) sẽ loại trừ một ngoại lệ.

Hiện tại, chuỗi đang chạy vòng lặp này có thuộc tính IsBackground được đặt thành true và do đó, ứng dụng xuất hiện để đóng đúng cách. Tuy nhiên, kể từ WaitHandle thực hiện IDisposable, tôi không chắc chắn nếu điều này được coi là kosher hoặc nếu đối tượng đó thực sự phải được xử lý trước khi thoát khỏi ứng dụng.

Đây có phải là thiết kế tồi không? Nếu không, kịch bản này thường được xử lý như thế nào?

Trả lời

8

Xác định thêm WaitHandle được gọi là _terminate sẽ báo hiệu yêu cầu chấm dứt vòng lặp và sau đó sử dụng WaitHandle.WaitAny thay vì WaitHandle.WaitOne.

var handles = { _request, _terminate }; 
while (WaitHandle.WaitAny(handles) == 0) 
{ 
    try 
    { 
    _workItem.Invoke(); 
    } 
    finally 
    { 
    OnCompleteWork(); 
    } 
} 
+0

Hoàn toàn hợp lý. Nhưng điều này có nghĩa rằng nó * không * OK chỉ đơn giản là có thread chạy vòng lặp này khởi tạo với 'IsBackground = true' (để khi ứng dụng thoát, vòng lặp kết thúc mặc dù' WaitHandle' vẫn đang chờ)? –

+0

@Dan: Điều đó có thể không sao, nhưng phương pháp này sẽ cho phép bạn kết thúc một cách duyên dáng luồng mà không phải tắt toàn bộ ứng dụng. –

+0

Xin chào. Tôi vừa hỏi bạn về một câu hỏi khác về mẫu này, Đây có phải là mẫu để dừng một luồng thông qua chờ xử lý không? (thay vì sử dụng hủy bỏ/ngắt)? –

1

Đặt thuộc tính IsBackground thành true ... nó sẽ tự động đóng chuỗi khi ứng dụng của bạn kết thúc.

Cách khác, bạn có thể làm gián đoạn luồng bằng cách gọi Thread.Interrupt và xử lý ThreadInterruptedException. Một ý tưởng khác là để gọi _request.Set() và làm cho vòng lặp while kiểm tra một lá cờ không ổn định để xác định xem ứng dụng được đóng hoặc nếu nó nên tiếp tục:

private volatile bool _running = true; 
while(_request.WaitOne() && _running) 
{ 
    //... 
} 

// somewhere else in the app 
_running = false; 
_request.Set(); 
+0

Vì vậy, bạn đang nói không cần phải gọi 'Đóng' trên' WaitHandle' miễn là chỉ là chuỗi nền? –

+0

@Dan, nó đã luôn luôn thoát ra một cách chính xác cho tôi ... Tôi đang tìm kiếm một cái gì đó trong tài liệu để trở lại tuyên bố đó lên. – Kiril

+0

Đây chính là điều giống như Thread.Abort(). Nó chỉ là CLR sẽ gọi nó. –

1

Khi một thread là chặn (bất kể những gì nó chặn trên), bạn có thể gọi Thread.Interrupt() Điều này sẽ gây ra ngoại lệ ThreadInterruptedException (Tôi tin rằng, nó có thể hơi khác một chút) Bạn có thể xử lý ngoại lệ này trên chính chuỗi đó và thực hiện bất kỳ việc dọn dẹp cần thiết nào.

Điều đáng chú ý là luồng sẽ chỉ ném ThreadInterruptedException khi nó đang chặn, nếu nó không chặn, nó sẽ không bị ném cho đến khi nó cố gắng chặn tiếp theo.

Đây là cách "an toàn" kết thúc chủ đề từ những gì tôi đã đọc về chủ đề này.

cũng đáng chú ý: Nếu đối tượng triển khai cả IDisposable và trình tinh chỉnh (nó sẽ nếu nó sử dụng tài nguyên không được quản lý), GC sẽ gọi trình hoàn tất thường hủy bỏ. Thông thường điều này là không xác định. Tuy nhiên, bạn có thể đảm bảo rằng họ sẽ được gọi về lối thoát ứng dụng. Chỉ trong những hoàn cảnh rất đặc biệt, họ sẽ không làm thế. (A. Môi trường mạng chấm dứt ngoại lệ như StackOverflowException được ném)

0

Tôi nghĩ rằng hệ điều hành sẽ làm sạch sau khi quá trình của bạn đã hoàn tất. Bởi vì thread của bạn được đánh dấu là IsBackground, CLR sẽ kết thúc quá trình và tất cả các luồng bên trong, vì vậy đây không phải là vấn đề.

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