其他形式的同步
我們可使用類Monitor與類Thread中的某些函數(shù),直接控制線程的同步,請看例1。
例1:
using namespace System;
using namespace System::Threading;
int main()
{
/*1*/ MessageBuffer^ m = gcnew MessageBuffer;
/*2a*/ ProcessMessages^ pm = gcnew ProcessMessages(m);
/*2b*/ Thread^ pmt = gcnew Thread(gcnew ThreadStart(pm,&ProcessMessages::ProcessMessagesEntryPoint));
/*2c*/ pmt->Start();
/*3a*/ CreateMessages^ cm = gcnew CreateMessages(m);
/*3b*/ Thread^ cmt = gcnew Thread(gcnew ThreadStart(cm, &CreateMessages::CreateMessagesEntryPoint));
/*3c*/ cmt->Start();
/*4*/ cmt->Join();
/*5*/ pmt->Interrupt();
/*6*/ pmt->Join();
Console::WriteLine("Primary thread terminating");
}
public ref class MessageBuffer
{
String^ messageText;
public:
void SetMessage(String^ s)
{
/*7*/ Monitor::Enter(this);
messageText = s;
/*8*/ Monitor::Pulse(this);
Console::WriteLine("Set new message {0}", messageText);
Monitor::Exit(this);
}
void ProcessMessages()
{
/*9*/ Monitor::Enter(this);
while (true)
{
try
{
/*10*/ Monitor::Wait(this);
}
catch (ThreadInterruptedException^ e)
{
Console::WriteLine("ProcessMessage interrupted");
return;
}
Console::WriteLine("Processed new message {0}", messageText);
}
Monitor::Exit(this);
}
};
public ref class CreateMessages
{
MessageBuffer^ msg;
public:
CreateMessages(MessageBuffer^ m)
{
msg = m;
}
void CreateMessagesEntryPoint()
{
for (int i = 1; i <= 5; ++i)
{
msg->SetMessage(String::Concat("M-", i.ToString()));
Thread::Sleep(2000);
}
Console::WriteLine("CreateMessages thread terminating");
}
};
public ref class ProcessMessages
{
MessageBuffer^ msg;
public:
ProcessMessages(MessageBuffer^ m)
{
msg = m;
}
void ProcessMessagesEntryPoint()
{
msg->ProcessMessages();
Console::WriteLine("ProcessMessages thread terminating");
}
};
在標記1中,創(chuàng)建一個MessageBuffer類型的共享緩沖區(qū);接著在標記2a、2b、2c中,創(chuàng)建了一個線程用于處理放置于緩沖區(qū)中的每條信息;標記3a、3b和3c,也創(chuàng)建了一個線程,并在共享緩沖區(qū)中放置了連續(xù)的5條信息以便處理。這兩個線程已被同步,因此處理者線程必須等到有"東西"放入到緩沖區(qū)中,才可以進行處理,且在前一條信息被處理完之前,不能放入第二條信息。在標記4中,將一直等待,直到創(chuàng)建者線程完成它的工作。
當標記5執(zhí)行時,處理者線程必須處理所有創(chuàng)建者線程放入的信息,因為使用了Thread::Interrupt讓其停止工作,并繼續(xù)等待標記6中調用的Thread::Join,這個函數(shù)允許調用線程阻塞它自己,直到其他線程結束。(一個線程可指定一個等待的時間,而不用無限等待下去。)
線程CreateMessages非常清晰明了,它向共享緩沖區(qū)中寫入了5條信息,并在每條信息之間等待2秒。為把一個線程掛起一個給定的時間(以毫秒計),我們調用了Thread::Sleep,在此,一個睡眠的線程可再繼續(xù)執(zhí)行,原因在于運行時環(huán)境,而不是另一個線程。
線程ProcessMessages甚至更加簡單,因為它利用了類MessageBuffer來做它的所有工作。類MessageBuffer中的函數(shù)是被同步的,因此在同一時間,只有一個函數(shù)能訪問共享緩沖區(qū)。
主程序首先啟動處理者線程,這個線程會執(zhí)行ProcessMessages,其將獲得父對象的同步鎖;然而,它立即調用了標記10中的Wait函數(shù),這個函數(shù)將讓它一直等待,直到再次被告之運行,期間,它也交出了同步鎖,這樣,允許創(chuàng)建者線程得到同步鎖并執(zhí)行SetMessage。一旦函數(shù)把新的信息放入到共享緩沖區(qū)中,就會調用標記8中的Pulse,其允許等待同步鎖的任意線程被喚醒,并繼續(xù)執(zhí)行下去。但是,在SetMessage執(zhí)行完成之前,這些都不可能發(fā)生,因為它在函數(shù)返回前都不可能交出同步鎖。如果情況一旦發(fā)生,處理者線程將重新得到同步鎖,并從標記10之后開始繼續(xù)執(zhí)行。此處要說明的是,一個線程即可無限等待,也可等到一個指定的時間到達。插1是程序的輸出。
插1:
Set new message M-1
Processed new message M-1
Set new message M-2
Processed new message M-2
Set new message M-3
Processed new message M-3
Set new message M-4
Processed new message M-4
Set new message M-5
Processed new message M-5
CreateMessages thread terminating
ProcessMessage interrupted
ProcessMessages thread terminating
Primary thread terminating
請仔細留意,處理者線程啟動于創(chuàng)建者線程之前。如果以相反的順序啟動,將會在沒有處理者線程等待的情況下,添加第一條信息,此時,沒有可供喚醒處理者線程,當處理者線程運行到它的第一個函數(shù)調用Wait時,將會錯過第一條信息,且只會在第二條信息存儲時被喚醒。
我們可使用類Monitor與類Thread中的某些函數(shù),直接控制線程的同步,請看例1。
例1:
using namespace System;
using namespace System::Threading;
int main()
{
/*1*/ MessageBuffer^ m = gcnew MessageBuffer;
/*2a*/ ProcessMessages^ pm = gcnew ProcessMessages(m);
/*2b*/ Thread^ pmt = gcnew Thread(gcnew ThreadStart(pm,&ProcessMessages::ProcessMessagesEntryPoint));
/*2c*/ pmt->Start();
/*3a*/ CreateMessages^ cm = gcnew CreateMessages(m);
/*3b*/ Thread^ cmt = gcnew Thread(gcnew ThreadStart(cm, &CreateMessages::CreateMessagesEntryPoint));
/*3c*/ cmt->Start();
/*4*/ cmt->Join();
/*5*/ pmt->Interrupt();
/*6*/ pmt->Join();
Console::WriteLine("Primary thread terminating");
}
public ref class MessageBuffer
{
String^ messageText;
public:
void SetMessage(String^ s)
{
/*7*/ Monitor::Enter(this);
messageText = s;
/*8*/ Monitor::Pulse(this);
Console::WriteLine("Set new message {0}", messageText);
Monitor::Exit(this);
}
void ProcessMessages()
{
/*9*/ Monitor::Enter(this);
while (true)
{
try
{
/*10*/ Monitor::Wait(this);
}
catch (ThreadInterruptedException^ e)
{
Console::WriteLine("ProcessMessage interrupted");
return;
}
Console::WriteLine("Processed new message {0}", messageText);
}
Monitor::Exit(this);
}
};
public ref class CreateMessages
{
MessageBuffer^ msg;
public:
CreateMessages(MessageBuffer^ m)
{
msg = m;
}
void CreateMessagesEntryPoint()
{
for (int i = 1; i <= 5; ++i)
{
msg->SetMessage(String::Concat("M-", i.ToString()));
Thread::Sleep(2000);
}
Console::WriteLine("CreateMessages thread terminating");
}
};
public ref class ProcessMessages
{
MessageBuffer^ msg;
public:
ProcessMessages(MessageBuffer^ m)
{
msg = m;
}
void ProcessMessagesEntryPoint()
{
msg->ProcessMessages();
Console::WriteLine("ProcessMessages thread terminating");
}
};
在標記1中,創(chuàng)建一個MessageBuffer類型的共享緩沖區(qū);接著在標記2a、2b、2c中,創(chuàng)建了一個線程用于處理放置于緩沖區(qū)中的每條信息;標記3a、3b和3c,也創(chuàng)建了一個線程,并在共享緩沖區(qū)中放置了連續(xù)的5條信息以便處理。這兩個線程已被同步,因此處理者線程必須等到有"東西"放入到緩沖區(qū)中,才可以進行處理,且在前一條信息被處理完之前,不能放入第二條信息。在標記4中,將一直等待,直到創(chuàng)建者線程完成它的工作。
當標記5執(zhí)行時,處理者線程必須處理所有創(chuàng)建者線程放入的信息,因為使用了Thread::Interrupt讓其停止工作,并繼續(xù)等待標記6中調用的Thread::Join,這個函數(shù)允許調用線程阻塞它自己,直到其他線程結束。(一個線程可指定一個等待的時間,而不用無限等待下去。)
線程CreateMessages非常清晰明了,它向共享緩沖區(qū)中寫入了5條信息,并在每條信息之間等待2秒。為把一個線程掛起一個給定的時間(以毫秒計),我們調用了Thread::Sleep,在此,一個睡眠的線程可再繼續(xù)執(zhí)行,原因在于運行時環(huán)境,而不是另一個線程。
線程ProcessMessages甚至更加簡單,因為它利用了類MessageBuffer來做它的所有工作。類MessageBuffer中的函數(shù)是被同步的,因此在同一時間,只有一個函數(shù)能訪問共享緩沖區(qū)。
主程序首先啟動處理者線程,這個線程會執(zhí)行ProcessMessages,其將獲得父對象的同步鎖;然而,它立即調用了標記10中的Wait函數(shù),這個函數(shù)將讓它一直等待,直到再次被告之運行,期間,它也交出了同步鎖,這樣,允許創(chuàng)建者線程得到同步鎖并執(zhí)行SetMessage。一旦函數(shù)把新的信息放入到共享緩沖區(qū)中,就會調用標記8中的Pulse,其允許等待同步鎖的任意線程被喚醒,并繼續(xù)執(zhí)行下去。但是,在SetMessage執(zhí)行完成之前,這些都不可能發(fā)生,因為它在函數(shù)返回前都不可能交出同步鎖。如果情況一旦發(fā)生,處理者線程將重新得到同步鎖,并從標記10之后開始繼續(xù)執(zhí)行。此處要說明的是,一個線程即可無限等待,也可等到一個指定的時間到達。插1是程序的輸出。
插1:
Set new message M-1
Processed new message M-1
Set new message M-2
Processed new message M-2
Set new message M-3
Processed new message M-3
Set new message M-4
Processed new message M-4
Set new message M-5
Processed new message M-5
CreateMessages thread terminating
ProcessMessage interrupted
ProcessMessages thread terminating
Primary thread terminating
請仔細留意,處理者線程啟動于創(chuàng)建者線程之前。如果以相反的順序啟動,將會在沒有處理者線程等待的情況下,添加第一條信息,此時,沒有可供喚醒處理者線程,當處理者線程運行到它的第一個函數(shù)調用Wait時,將會錯過第一條信息,且只會在第二條信息存儲時被喚醒。

