2010-03-19 59 views
32

我主要關注的是布爾標誌......可以安全地使用它,而無需任何同步?我已經在幾個地方看過它是原子的(包括文檔)。是否安全使用布爾標誌來阻止線程在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(); 
    } 
} 

這是以這種方式設計任務的安全方法嗎?任何建議,改進,意見?

注意:我寫了我的自定義CountDownLatch類,以防您想知道我從哪裏得到它。

更新:
這裏是我的CountDownLatch太:

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(); 
    } 
} 

回答

45

你最好將其標記volatile雖然:

volatile關鍵字表示可能會修改 字段b y多個 併發執行線程。被聲明爲易失性的字段 不是 受制於編譯器優化, 假定由單個線程訪問。這個 確保最新的值 始終存在於現場。

但我會改變你的循環:

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

因爲它是在您的文章中「一些代碼」可能執行當線程停止(即當停止被調用。),這是意外,甚至可能是不正確的。

+0

@Remus在我的while循環代碼順序中很好的捕獲。關於揮發性標誌:如果在這種情況下標誌是揮發性的,它是否真的有所作爲?如果它錯過了第一次更新,那麼它將在循環中再次執行一次,並在下一次執行時捕獲它。 – Kiril 2010-03-19 04:31:12

+6

如果您不標記爲volatile,生成的代碼可能會將值優化爲註冊表,並且您的線程將永遠不會*看到變化。 – 2010-03-19 04:34:25

+4

@Remus我現在明白了:原子性與線程之間的可見性無關...僅僅因爲一個操作在一個CPU週期內執行,並不意味着結果對其他線程可見,除非值被標記爲易失性。 – Kiril 2010-03-19 04:40:37

4

布爾是C#中的原子,但是,如果您想在一個線程中修改它並在另一個線程中讀取它,則至少需要將其標記爲volatile。否則讀線程只能實際讀取一次到寄存器中。

+0

@Michael在循環捕獲之前,循環中有多少個循環可以實際通過?我認爲這將是一個,但我認爲沒有這樣的保證...... – Kiril 2010-03-19 04:35:01

+0

@Michael我現在明白了:我只是意識到,我是混淆原子性與知名度。 – Kiril 2010-03-19 04:42:50

0

BTW,我只注意到了這部分代碼:

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

您需要解鎖工作線程兩次使用「startSignal.Set()」的同時,塊內的代碼來執行。

這是故意的嗎?

+2

@Akapetronics startSignal只需要設置一次。 WaitOne不重置事件,所以我可以多次調用startSignal.WaitOne(),並且不會阻塞,直到調用startSignal.Reset()。該設計是故意的:線程將阻塞,直到Start方法被調用,並且Start方法首先設置運行標誌並設置startSignal,以便while循環可以開始執行。如果我逆轉順序並首先設置startSignal,那麼while循環可能會在我設置運行標誌之前退出。注意這個設計消除了對鎖的需要。 – Kiril 2010-03-19 05:15:56