2010-03-19 1 views
32

Mối quan tâm chính của tôi là với cờ boolean ... có an toàn khi sử dụng nó mà không cần đồng bộ hóa không? Tôi đã đọc ở một số nơi mà nó nguyên tử (bao gồm cả tài liệu).Có an toàn khi sử dụng cờ boolean để ngăn chặn một luồng chạy trong C#

class MyTask 
{ 
    private ManualResetEvent startSignal; 
    private CountDownLatch latch; 
    private bool running; 

    MyTask(CountDownLatch latch) 
    { 
     running = false; 
     this.latch = latch; 
     startSignal = new ManualResetEvent(false); 
    } 

    // A method which runs in a thread 
    public void Run() 
    { 
     startSignal.WaitOne(); 
     while(running) 
     { 
      startSignal.WaitOne(); 
      //... some code 
     } 
     latch.Signal(); 
    } 

    public void Stop() 
    { 
     running = false; 
     startSignal.Set(); 
    } 

    public void Start() 
    { 
     running = true; 
     startSignal.Set(); 
    } 

    public void Pause() 
    { 
     startSignal.Reset(); 
    } 

    public void Resume() 
    { 
     startSignal.Set(); 
    } 
} 

Đây có phải là cách an toàn để thiết kế tác vụ theo cách này không? Bất kỳ đề xuất, cải tiến, ý kiến?

Lưu ý: Tôi đã viết lớp tùy chỉnh CountDownLatch trong trường hợp bạn tự hỏi mình đang lấy nó từ đâu.

Cập nhật:
Đây là CountDownLatch tôi quá:

public class CountDownLatch 
{ 
    private volatile int m_remain; 
    private EventWaitHandle m_event; 

    public CountDownLatch (int count) 
    { 
     if (count < 0) 
      throw new ArgumentOutOfRangeException(); 
     m_remain = count; 
     m_event = new ManualResetEvent(false); 
     if (m_remain == 0) 
     { 
      m_event.Set(); 
     } 
    } 

    public void Signal() 
    { 
     // The last thread to signal also sets the event. 
     if (Interlocked.Decrement(ref m_remain) == 0) 
      m_event.Set(); 
    } 

    public void Wait() 
    { 
     m_event.WaitOne(); 
    } 
} 

Trả lời

45

Bạn tốt hơn đánh dấu nó volatile mặc dù:

Các từ khóa dễ bay hơi chỉ ra rằng một trường có thể được sửa đổi b y nhiều đồng thời thực hiện chuỗi. Các trường được khai báo dễ bay hơi không phải là tùy thuộc vào tối ưu hóa trình biên dịch mà giả sử truy cập bằng một chuỗi duy nhất. Điều này đảm bảo rằng giá trị cập nhật nhất luôn có mặt tại hiện trường.

Nhưng tôi sẽ thay đổi vòng lặp của bạn:

startSignal.WaitOne(); 
    while(running) 
    { 
     //... some code 
     startSignal.WaitOne(); 
    } 

Vì nó là trong bài viết của bạn 'một số mã' có thể thực hiện khi chủ đề là ngừng (tức là khi dừng được gọi.) Là bất ngờ và thậm chí có thể không chính xác.

+0

@Remus Tốt bắt trong trật tự mã vòng lặp while của tôi. Liên quan đến lá cờ dễ bay hơi: liệu nó có thực sự tạo ra sự khác biệt nếu lá cờ biến động trong trường hợp này? Nếu nó bỏ lỡ bản cập nhật đầu tiên, sau đó nó sẽ thực hiện thêm một lần nữa thông qua vòng lặp và bắt nó lần sau ... – Kiril

+6

Nếu bạn không đánh dấu nó dễ bay hơi, mã được tạo ra có thể tối ưu hóa giá trị vào sổ đăng ký và chuỗi sẽ * không bao giờ * thấy thay đổi. –

+4

@Remus Tôi nhận được nó ngay bây giờ: atomicity không có gì để làm với khả năng hiển thị giữa các chủ đề ... chỉ vì một hoạt động được thực hiện trong một chu kỳ CPU nó không có nghĩa là kết quả sẽ được hiển thị cho các chủ đề khác trừ khi giá trị được đánh dấu dễ bay hơi. – Kiril

4

Booleans là nguyên tử trong C#, tuy nhiên, nếu bạn muốn sửa đổi nó trong một chủ đề và đọc nó trong một chủ đề khác, bạn sẽ cần phải đánh dấu nó dễ bay hơi ở rất ít nhất ,. Nếu không thì luồng đọc chỉ có thể đọc nó một lần vào thanh ghi.

+0

@Michael Vì vậy, có bao nhiêu chu kỳ thông qua vòng lặp thực tế có thể vượt qua trước khi vòng lặp bắt nó? Tôi giả định rằng nó sẽ là một, nhưng tôi cho rằng không có sự bảo đảm như vậy ... – Kiril

+0

@Michael Tôi đã nhận nó ngay bây giờ: Tôi chỉ nhận ra rằng tôi đã nhầm lẫn nguyên tử với khả năng hiển thị. – Kiril

0

BTW, tôi chỉ chú ý phần này của mã:

// A method which runs in a thread 
    public void Run() 
    { 
     startSignal.WaitOne(); 
     while(running) 
     { 
      startSignal.WaitOne(); 
      //... some code 
     } 
     latch.Signal(); 
    } 

Bạn sẽ cần phải mở khóa các sợi nhân hai lần sử dụng "startSignal.Set()" cho mã trong khối thời gian để thực hiện.

Điều này có chủ ý không?

+2

@Akapetronics startSignal chỉ cần được đặt một lần. WaitOne không thiết lập lại sự kiện, vì vậy tôi có thể gọi startSignal.WaitOne() nhiều lần và nó sẽ không chặn cho đến khi startSignal.Reset() được gọi. Thiết kế có chủ ý: thread sẽ chặn cho đến khi phương thức Start được gọi và phương thức Start trước tiên đặt cờ đang chạy và đặt startSignal để vòng lặp while có thể bắt đầu thực hiện. Nếu tôi đảo ngược thứ tự và đầu tiên thiết lập startSignal thì vòng lặp while có thể thoát ra trước khi tôi thiết lập cờ chạy. Lưu ý rằng thiết kế này giúp loại bỏ sự cần thiết của khóa. – Kiril