在前面的多線程編程系列的文章中,咱們瞭解了在.NET中多線程編程必需要掌握的基本知識,可是可能你們看了文章以後,感受仍是很模糊,對一個具體的編程可能仍是以爲無從下手,究其緣由多是理論講的過多,而沒有太多的實際參考例子,形成收穫不大。所以,在接下來的文章中,我將給出幾個典型的多線程編程的實例,讓你們有更清楚的認識。
Case 1 - No synchronization
在咱們的第一個例子中,有兩類線程,兩個是讀線程,一個是寫線程,兩個線程是並行運行的而且須要訪問同一個共享資源。讀線程在寫線程以前啓動,用於設置共享變量的值。我使用Thread.Sleep來完成這些工做。摘錄代碼以下:
Thread t0 = new Thread(new ThreadStart(WriteThread));
Thread t1 = new Thread(new ThreadStart(ReadThread10));
Thread t2 = new Thread(new ThreadStart(ReadThread20));
t0.IsBackground=true;
t1.IsBackground=true;
t2.IsBackground=true;
t0.Start();
t1.Start();
t2.Start();
正如所看到的那樣,讀線程啓動以後當即啓動兩個寫線程。下面的代碼是兩個讀線程和寫線程所執行的代碼。
public void WriteThread()
{
Thread.Sleep(1000);
m_x=3;
}
public void ReadThread10()
{
int a = 10;
for(int y=0;y<5;y++)
{
string s = "ReadThread10";
s = s + " # multiplier= ";
s = s + Convert.ToString(a) + " # ";
s = s + a * m_x;
listBox1.Items.Add(s);
Thread.Sleep(1000);
}
}
public void ReadThread20()
{
int a = 20;
for(int y=0;y<5;y++)
{
string s = "ReadThread20";
s = s + " # multiplier= ";
s = s + Convert.ToString(a) + " # ";
s = s + a * m_x;
listBox1.Items.Add(s);
Thread.Sleep(1000);
}
}
最後運行的結果以下:
經過上面的運行結果,咱們能夠明顯的看出運行結果並非咱們所指望的那樣,開始的兩個結果,讀線程運行在寫線程以前,這是咱們極力要避免發生的事情。
Case 2 - Synchronization [One WriteThread - Many ReadThreads]
下面我將使用ManualResetEvent來解決上面遇到的問題來達到線成的同步,惟一不一樣的是咱們在啓動讀線程和寫線程以前使用安全的方法。
Thread t0 = new Thread(new ThreadStart(SafeWriteThread));
Thread t1 = new Thread(new ThreadStart(SafeReadThread10));
Thread t2 = new Thread(new ThreadStart(SafeReadThread20));
t0.IsBackground=true;
t1.IsBackground=true;
t2.IsBackground=true;
t0.Start();
t1.Start();
t2.Start();
添加一個ManualResetEvent:
m_mre = new ManualResetEvent(false);
看看SafeWriteThread的代碼:
public void SafeWriteThread()
{
m_mre.Reset();
WriteThread();
m_mre.Set();
}
Reset設置ManualResetEvent的狀態爲non-signaled,這意味着事件沒有發生。接着咱們來調用WriteThread方法,實際上能夠跳過Reset這一步,由於咱們在ManualResetEvent的構造函數設置其狀態爲non-signaled。一旦WriteThread線程返回,調用Set方法設置ManualResetEvent的狀態爲signaled。
下面讓咱們來看看另外兩個SafeReadThread方法:
public void SafeReadThread10()
{
m_mre.WaitOne();
ReadThread10();
}
public void SafeReadThread20()
{
m_mre.WaitOne();
ReadThread20();
}
WaitOne方法將阻塞當前的線程直到ManualResetEvent的狀態被設置爲signaled。在這裏,咱們程序中的兩個讀線程都將阻塞至SafeWriteThread完成任務後調用Set方法。這樣咱們就確保了兩個讀線程在寫線程完成對共享資源的訪問以後才執行。
Case 3 - Synchronization [Many WriteThreads - Many ReadThreads]
下面咱們將模擬更爲複雜的情形。在下面的程序中,有多個寫線程和讀線程。讀線程只有在全部的寫線程完成了任務以後才能訪問共享資源。在實際的狀況中,讀線程多是並行的運行,可是爲了簡便起見,我使寫線程運行有必定的順序,只有在前一個寫線程完成以後,第二個寫線程才能啓動。
在這裏,我增長了一個ManualResetEvent對象和ManualResetEvent的數組。
public ManualResetEvent m_mreB;
public ManualResetEvent[] m_mre_array;
添加初始化代碼:
m_mreB = new ManualResetEvent(false);
m_mre_array = new ManualResetEvent[2];
m_mre_array[0]=m_mre;
m_mre_array[1]=m_mreB;
啓動四個線程:
Thread t0 = new Thread(new ThreadStart(SafeWriteThread));
Thread t0B = new Thread(new ThreadStart(SafeWriteThreadB));
Thread t1 = new Thread(new ThreadStart(SafeReadThread10B));
Thread t2 = new Thread(new ThreadStart(SafeReadThread20B));
t0.IsBackground=true;
t0B.IsBackground=true;
t1.IsBackground=true;
t2.IsBackground=true;
t0.Start();
t0B.Start();
t1.Start();
t2.Start();
在這裏有兩個StartThreads和兩個WriteThreads,讓咱們看看他們的執行:
public void SafeWriteThread()
{
m_mre.Reset();
WriteThread();
m_mre.Set();
}
public void SafeWriteThreadB()
{
m_mreB.Reset();
m_mre.WaitOne();
Thread.Sleep(1000);
m_x+=3;
m_mreB.Set();
}
我對第二個WriteThread使用了另一個事件對象,爲了模擬等待第一個線程完成工做。
public void SafeReadThread10B()
{
WaitHandle.WaitAll(m_mre_array);
ReadThread10();
}
public void SafeReadThread20B()
{
WaitHandle.WaitAll(m_mre_array);
ReadThread20();
}
在這裏,使用了一個WaitAll的方法,他是WaitHandle基類提供給ManualResetEvent的靜態方法,它的參數爲咱們在前面定義的ManualResetEvent數組。他阻塞當前的線程直到參數數組裏面全部的ManualResetEvent對象設置狀態爲signaled,換一句話說就是等待他們完成了各自的任務。