一:終止狀態和非終止狀態
首先說說線程的終止狀態和非終止狀態。AutoResetEvent和ManualResetEvent的構造函數中,都有bool變量來指明線程的終止狀態和非終止狀態。true表示終止狀態(個人理解也就是可運行狀態,根據理解應該是該線程的阻塞終止了),false表示非終止狀態。
AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread.Sleep(3000); //Thread.Sleep(Int32)是批當前進程掛起3000毫秒,與線程t1是一點關系也沒有的。
_autoResetEvent.Set();
}
void Thread1Foo()
{
_autoResetEvent.WaitOne();
MessageBox.Show("t1 end");
}
這段代碼的執行結果,就是3秒鍾過后,彈出“t1 end”。
而如果把:
AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
改為:
AutoResetEvent _autoResetEvent = new AutoResetEvent(true);
則“t1 end”將會立刻彈出。
也就是說,在終止狀態中,_autoResetEvent.WaitOne()是不會起到阻滯工作線程的作用的。(PS:ManualResetEvent也同樣)
二:AutoResetEvent和ManualResetEvent的區別
AutoResetEvent 允許線程通過發信號互相通信。 通常,當線程需要獨占訪問資源時使用該類。
線程通過調用 AutoResetEvent 上的 WaitOne 來等待信號。 如果 AutoResetEvent 為非終止狀態,則線程會被阻止,並等待當前控制資源的線程通過調用 Set來通知資源可用。
調用 Set 向 AutoResetEvent 發信號以釋放等待線程。 AutoResetEvent 將保持終止狀態,直到一個正在等待的線程被釋放,然后自動返回非終止狀態。 如果沒有任何線程在等待,則狀態將無限期地保持 為終止狀態。如果當 AutoResetEvent 為終止狀態時線程調用 WaitOne,則線程不會被阻止。 AutoResetEvent 將立即釋放線程並返回到非終止狀態。
AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread t2 = new Thread(this.Thread2Foo);
t2.Start();
Thread.Sleep(3000);
_autoResetEvent.Set();
}
void Thread1Foo()
{
_autoResetEvent.WaitOne();
MessageBox.Show("t1 end");
}
void Thread2Foo()
{
_autoResetEvent.WaitOne();
MessageBox.Show("t2 end");
}
該段代碼運行的效果是,過3秒后,要么彈出“t1 end”,要么彈出“t2 end”,不會兩個都彈出。也就是說,其中一個進行將會結束,而另一個進程永遠不會結束。
代碼段3:
ManualResetEvent _menuRestEvent = new ManualResetEvent(false);
private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread t2 = new Thread(this.Thread2Foo);
t2.Start();
Thread.Sleep(3000);
_menuRestEvent.Set();
}
void Thread1Foo()
{
_menuRestEvent.WaitOne();
MessageBox.Show("t1 end");
}
void Thread2Foo()
{
_menuRestEvent.WaitOne();
MessageBox.Show("t2 end");
}
該段代碼運行的效果是,過3秒后,“t1 end”和“t2 end”,兩個都被彈出。也就是說,兩個進程都結束了。
這個特性就是說,AutoResetEvent只會給一個線程發送信號,而不會給多個線程發送信號。在我們需要同步多個線程的時候,就只能采用ManualResetEvent了。至於深層次的原因是,AutoResetEvent在set()之后,會將線程 狀態自動置為false,而ManualResetEvent在Set()后,線程的狀態就變為true了,必須手動ReSet()之后,才會重新將線程置為false。這也就是為什么他們的名字一個為Auto,一個為Manual的原因。為了更加充分的驗證Manua lResetEvent的這點特性,我們再來看代碼片段4
代碼片段4:
ManualResetEvent _menuRestEvent = new ManualResetEvent(false);
private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread t2 = new Thread(this.Thread2Foo);
t2.Start();
Thread.Sleep(3000);
_menuRestEvent.Set();
//_menuRestEvent.Reset();
}
void Thread1Foo()
{
_menuRestEvent.WaitOne();
MessageBox.Show("t1 step1 end");
//睡1S,用於等待主線程_menuRestEvent.Reset();
Thread.Sleep(1000);
_menuRestEvent.WaitOne();
MessageBox.Show("t1 step2 end");
}
void Thread2Foo()
{
_menuRestEvent.WaitOne();
MessageBox.Show("t2 step1 end");
//睡1S,用於等待主線程_menuRestEvent.Reset();
Thread.Sleep(1000);
_menuRestEvent.WaitOne();
MessageBox.Show("t2 step2 end");
}
在代碼片段4中,我們對//_menuRestEvent.Reset()進行了注釋,也就是說, _menuRestEvent.Set()后,線程的狀態就是true狀態的,程序運行的結果是"t1 step1 end"、"t1 step2 end"、"t1 step2 end"、"t2 step2 end"在3秒之后全部彈出。
而如果我們將//_menuRestEvent.Reset()的注釋去掉,會發現"t1 step2 end"和"t2 step2 end"永遠不會彈出。除非我們在主線程中再次對_menuRestEvent進行Set()。
