2012-08-14 27 views
12

Tôi có một đối tượng trong một chuỗi công nhân, mà tôi có thể hướng dẫn ngừng chạy. Tôi có thể thực hiện điều này bằng cách sử dụng bool hoặc một AutoResetEvent:AutoResetEvent so với boolean để dừng một sợi

boolean:

private volatile bool _isRunning; 

public void Run() { 
    while (_isRunning) 
    { 
     doWork(); 
     Thread.Sleep(1000); 
    } 
} 

AutoResetEvent:

private AutoResetEvent _stop; 

public void Run() { 
    do { 
     doWork(); 
    } while (!_stop.WaitOne(1000)); 
} 

Phương pháp Stop() sau đó sẽ thiết lập _isRunning false, hoặc gọi _stop.Set().

Ngoài ra, giải pháp với AutoResetEvent có thể dừng nhanh hơn một chút, có sự khác biệt nào giữa phương pháp này không? Cái này tốt hơn những cái khác phải không?

+2

Tôi sẽ sử dụng bộ hẹn giờ chứ không phải ngủ. Để kết thúc, bạn có thể dừng bộ hẹn giờ. – Servy

+0

Tôi đã thêm một số quan sát để có thể trả lời, về cơ bản tôi không nghĩ rằng những đoạn mã này làm những gì bạn muốn. – Jodrell

Trả lời

11

C# dễ bay hơi không cung cấp tất cả các bảo đảm. Nó may vẫn đọc dữ liệu cũ. Tốt hơn nên sử dụng cơ chế đồng bộ hóa hệ điều hành cơ bản vì nó cung cấp sự bảo đảm mạnh mẽ hơn nhiều.

Tất cả điều này một cách sâu sắc discussed bởi Eric Lippert (thực sự đáng để đọc), đây là trích dẫn ngắn:

Trong C#, "không ổn định" có nghĩa là không chỉ "đảm bảo rằng trình biên dịch và jitter không thực hiện bất kỳ mã nào sắp xếp lại hoặc đăng ký bộ nhớ đệm tối ưu hóa trên biến này ".Nó cũng có nghĩa là "yêu cầu các bộ vi xử lý làm bất cứ điều gì họ cần làm để đảm bảo rằng tôi đang đọc giá trị mới nhất, ngay cả khi điều đó có nghĩa là dừng các bộ vi xử lý khác và thực hiện chúng đồng bộ bộ nhớ chính với bộ đệm của chúng".

Thực ra, bit cuối cùng đó là một lời nói dối. Các ngữ nghĩa thực sự của đọc dễ bay hơi và viết là phức tạp hơn đáng kể so với tôi đã nêu ở đây; trong thực tế họ không thực sự đảm bảo rằng mọi bộ xử lý dừng lại những gì nó đang thực hiện và cập nhật bộ nhớ cache đến/từ bộ nhớ chính. Thay vào đó, , chúng cung cấp đảm bảo yếu hơn về cách truy cập bộ nhớ trước và sau khi đọc và viết có thể được quan sát để được đặt hàng đối với nhau. Một số hoạt động nhất định như tạo chủ đề mới, nhập khóa hoặc bằng cách sử dụng một trong các nhóm phương pháp được liên kết giới thiệu mạnh mẽ hơn đảm bảo về quan sát đặt hàng. Nếu bạn muốn biết thêm chi tiết, đọc các phần 3.10 và 10.5.3 của đặc tả C# 4.0.

Thành thật mà nói, Tôi không khuyến khích bạn tạo ra một trường dễ bay hơi. Các lĩnh vực dễ bay hơi là dấu hiệu cho thấy bạn đang làm điều gì đó hết sức điên rồ: bạn đang cố đọc và viết cùng một giá trị trên hai chủ đề khác nhau mà không đặt khóa tại chỗ. Khóa đảm bảo rằng bộ nhớ đọc hoặc sửa đổi bên trong khóa được quan sát là nhất quán, bảo đảm khóa chỉ có một luồng truy cập một bộ nhớ nhất định tại một thời điểm và do đó, bật.

+0

+1 vì điều này nhắc lại điểm đầu tiên của tôi chi tiết hơn – Jodrell

+0

Các công trình dễ bay hơi ở đây tốt. Bất kỳ thread nào ghi vào một đều có tất cả các cập nhật bộ nhớ của nó được đẩy vào bộ nhớ chính. Bất kỳ bộ đọc luồng nào đều có bộ nhớ làm việc được làm mới từ chính. Vì vậy, một _write_ đến một biến động (và dữ liệu nó tham khảo) sẽ được nhìn thấy bởi bất kỳ _read_ từ đó dễ bay hơi. Điều đó nói rằng, AutoResetEvent trông tốt hơn cho tôi trong trường hợp này. – RalphChapin

+1

@RalphChapin AFAIK trên một hệ thống đa bộ xử lý chỉ có * yếu * bảo đảm rằng * tất cả * bộ xử lý sẽ thấy giá trị được cập nhật. – oleksii

3

IMHO số AutoResetEvent là tốt hơn, bởi vì bạn không thể quên từ khóa quan trọng volatile trong trường hợp này.

0

Điều đó phụ thuộc nếu đoạn mã trên là tất cả những gì bạn đang làm hay không. Sự kiện AutoReset, như tên gọi của nó, được đặt lại sau khi WaitOne được thông qua. Điều này có nghĩa là bạn có thể sử dụng nó một lần nữa ngay lập tức, thay vì với bool, phải đặt nó trở lại thành sự thật.

1

Trước khi sử dụng từ khóa volatile, bạn nên đọc this và, tôi đoán, khi nghiên cứu đa luồng, bạn có thể đọc toàn bộ bài viết http://www.albahari.com/threading/.

Nó giải thích sự tinh tế của từ khóa volatile và tại sao hành vi của nó có thể là bất ngờ.


Bạn sẽ lưu ý rằng khi sử dụng volatile, đọc và viết có thể được sắp xếp lại có thể dẫn đến một sự lặp lại thêm trong các tình huống đồng thời chặt chẽ. Trong trường hợp này, bạn có thể phải đợi thêm một giây nữa.


Sau khi xem, tôi không nghĩ rằng mã của bạn hoạt động vì nhiều lý do,

Các "boolean:" Đoạn luôn ngủ cho khoảng một giây, có lẽ không phải những gì bạn muốn.

Đoạn mã "AutoResetEvent:" không khởi tạo _stop và sẽ luôn chạy doWork() ít nhất một lần.

+0

Tôi đã quên một '!' Trên _stop.WaitOne(). Những ví dụ này được đơn giản hóa một chút, nhưng về cơ bản tôi muốn chạy 'doWork()' định kỳ. 'DoWork()' sẽ chạy cho đến khi nó hết hoạt động, sau đó nó phải đợi một lúc để công việc mới tích lũy. – Sjoerd

+0

@Sjoerd, tôi đã chỉnh sửa phần cuối cùng của mình.Sự thận trọng xung quanh 'bay hơi' vẫn còn thích hợp. – Jodrell

6

Dễ bay hơi không đủ tốt, nhưng thực tế nó sẽ luôn hoạt động vì bộ lập lịch hệ điều hành sẽ luôn luôn có khóa. Và sẽ làm việc tốt trên một lõi với một mô hình bộ nhớ mạnh mẽ, giống như x86 mà đốt cháy rất nhiều nước để giữ cho bộ nhớ cache được đồng bộ giữa các lõi.

Vì vậy, vấn đề thực sự chỉ là tốc độ một luồng sẽ phản hồi yêu cầu dừng. Nó rất dễ dàng để đo lường, chỉ cần bắt đầu một đồng hồ bấm giờ trong chủ đề kiểm soát và ghi lại thời gian sau khi vòng lặp while trong chuỗi công nhân. Kết quả tôi đo từ lặp đi lặp lại lấy 1000 mẫu và lấy trung bình, lặp đi lặp lại 10 lần:

volatile bool, x86:   550 nanoseconds 
volatile bool, x64:   550 nanoseconds 
ManualResetEvent, x86:  2270 nanoseconds 
ManualResetEvent, x64:  2250 nanoseconds 
AutoResetEvent, x86:  2500 nanoseconds 
AutoResetEvent, x64:  2100 nanoseconds 
ManualResetEventSlim, x86: 650 nanoseconds 
ManualResetEventSlim, x64: 630 nanoseconds 

Ghi chú rằng các kết quả cho bool dễ bay hơi rất khó có thể tìm thấy tốt trên một bộ xử lý với một mô hình bộ nhớ yếu, như ARM hoặc Itanium. Tôi không có ai để kiểm tra.

Rõ ràng có vẻ như bạn muốn ưu tiên ManualResetEventSlim, mang đến sự hoàn hảo và đảm bảo tốt.

Một lưu ý với những kết quả này, chúng được đo bằng chuỗi công nhân chạy vòng lặp nóng, liên tục kiểm tra điều kiện dừng và không thực hiện bất kỳ công việc nào khác. Đó không phải là một kết hợp tốt với mã thực, một chuỗi thường sẽ không kiểm tra điều kiện dừng thường xuyên. Mà làm cho sự khác biệt giữa các kỹ thuật này phần lớn là không quan trọng.

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