其他形式的同步
我們可使用類Monitor與類Thread中的某些函數(shù),直接控制線程的同步,請(qǐng)看例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");
}
};
在標(biāo)記1中,創(chuàng)建一個(gè)MessageBuffer類型的共享緩沖區(qū);接著在標(biāo)記2a、2b、2c中,創(chuàng)建了一個(gè)線程用于處理放置于緩沖區(qū)中的每條信息;標(biāo)記3a、3b和3c,也創(chuàng)建了一個(gè)線程,并在共享緩沖區(qū)中放置了連續(xù)的5條信息以便處理。這兩個(gè)線程已被同步,因此處理者線程必須等到有"東西"放入到緩沖區(qū)中,才可以進(jìn)行處理,且在前一條信息被處理完之前,不能放入第二條信息。在標(biāo)記4中,將一直等待,直到創(chuàng)建者線程完成它的工作。
當(dāng)標(biāo)記5執(zhí)行時(shí),處理者線程必須處理所有創(chuàng)建者線程放入的信息,因?yàn)槭褂昧薚hread::Interrupt讓其停止工作,并繼續(xù)等待標(biāo)記6中調(diào)用的Thread::Join,這個(gè)函數(shù)允許調(diào)用線程阻塞它自己,直到其他線程結(jié)束。(一個(gè)線程可指定一個(gè)等待的時(shí)間,而不用無限等待下去。)
線程CreateMessages非常清晰明了,它向共享緩沖區(qū)中寫入了5條信息,并在每條信息之間等待2秒。為把一個(gè)線程掛起一個(gè)給定的時(shí)間(以毫秒計(jì)),我們調(diào)用了Thread::Sleep,在此,一個(gè)睡眠的線程可再繼續(xù)執(zhí)行,原因在于運(yùn)行時(shí)環(huán)境,而不是另一個(gè)線程。
線程ProcessMessages甚至更加簡(jiǎn)單,因?yàn)樗昧祟怣essageBuffer來做它的所有工作。類MessageBuffer中的函數(shù)是被同步的,因此在同一時(shí)間,只有一個(gè)函數(shù)能訪問共享緩沖區(qū)。
主程序首先啟動(dòng)處理者線程,這個(gè)線程會(huì)執(zhí)行ProcessMessages,其將獲得父對(duì)象的同步鎖;然而,它立即調(diào)用了標(biāo)記10中的Wait函數(shù),這個(gè)函數(shù)將讓它一直等待,直到再次被告之運(yùn)行,期間,它也交出了同步鎖,這樣,允許創(chuàng)建者線程得到同步鎖并執(zhí)行SetMessage。一旦函數(shù)把新的信息放入到共享緩沖區(qū)中,就會(huì)調(diào)用標(biāo)記8中的Pulse,其允許等待同步鎖的任意線程被喚醒,并繼續(xù)執(zhí)行下去。但是,在SetMessage執(zhí)行完成之前,這些都不可能發(fā)生,因?yàn)樗诤瘮?shù)返回前都不可能交出同步鎖。如果情況一旦發(fā)生,處理者線程將重新得到同步鎖,并從標(biāo)記10之后開始繼續(xù)執(zhí)行。此處要說明的是,一個(gè)線程即可無限等待,也可等到一個(gè)指定的時(shí)間到達(dá)。插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
請(qǐng)仔細(xì)留意,處理者線程啟動(dòng)于創(chuàng)建者線程之前。如果以相反的順序啟動(dòng),將會(huì)在沒有處理者線程等待的情況下,添加第一條信息,此時(shí),沒有可供喚醒處理者線程,當(dāng)處理者線程運(yùn)行到它的第一個(gè)函數(shù)調(diào)用Wait時(shí),將會(huì)錯(cuò)過第一條信息,且只會(huì)在第二條信息存儲(chǔ)時(shí)被喚醒。
我們可使用類Monitor與類Thread中的某些函數(shù),直接控制線程的同步,請(qǐng)看例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");
}
};
在標(biāo)記1中,創(chuàng)建一個(gè)MessageBuffer類型的共享緩沖區(qū);接著在標(biāo)記2a、2b、2c中,創(chuàng)建了一個(gè)線程用于處理放置于緩沖區(qū)中的每條信息;標(biāo)記3a、3b和3c,也創(chuàng)建了一個(gè)線程,并在共享緩沖區(qū)中放置了連續(xù)的5條信息以便處理。這兩個(gè)線程已被同步,因此處理者線程必須等到有"東西"放入到緩沖區(qū)中,才可以進(jìn)行處理,且在前一條信息被處理完之前,不能放入第二條信息。在標(biāo)記4中,將一直等待,直到創(chuàng)建者線程完成它的工作。
當(dāng)標(biāo)記5執(zhí)行時(shí),處理者線程必須處理所有創(chuàng)建者線程放入的信息,因?yàn)槭褂昧薚hread::Interrupt讓其停止工作,并繼續(xù)等待標(biāo)記6中調(diào)用的Thread::Join,這個(gè)函數(shù)允許調(diào)用線程阻塞它自己,直到其他線程結(jié)束。(一個(gè)線程可指定一個(gè)等待的時(shí)間,而不用無限等待下去。)
線程CreateMessages非常清晰明了,它向共享緩沖區(qū)中寫入了5條信息,并在每條信息之間等待2秒。為把一個(gè)線程掛起一個(gè)給定的時(shí)間(以毫秒計(jì)),我們調(diào)用了Thread::Sleep,在此,一個(gè)睡眠的線程可再繼續(xù)執(zhí)行,原因在于運(yùn)行時(shí)環(huán)境,而不是另一個(gè)線程。
線程ProcessMessages甚至更加簡(jiǎn)單,因?yàn)樗昧祟怣essageBuffer來做它的所有工作。類MessageBuffer中的函數(shù)是被同步的,因此在同一時(shí)間,只有一個(gè)函數(shù)能訪問共享緩沖區(qū)。
主程序首先啟動(dòng)處理者線程,這個(gè)線程會(huì)執(zhí)行ProcessMessages,其將獲得父對(duì)象的同步鎖;然而,它立即調(diào)用了標(biāo)記10中的Wait函數(shù),這個(gè)函數(shù)將讓它一直等待,直到再次被告之運(yùn)行,期間,它也交出了同步鎖,這樣,允許創(chuàng)建者線程得到同步鎖并執(zhí)行SetMessage。一旦函數(shù)把新的信息放入到共享緩沖區(qū)中,就會(huì)調(diào)用標(biāo)記8中的Pulse,其允許等待同步鎖的任意線程被喚醒,并繼續(xù)執(zhí)行下去。但是,在SetMessage執(zhí)行完成之前,這些都不可能發(fā)生,因?yàn)樗诤瘮?shù)返回前都不可能交出同步鎖。如果情況一旦發(fā)生,處理者線程將重新得到同步鎖,并從標(biāo)記10之后開始繼續(xù)執(zhí)行。此處要說明的是,一個(gè)線程即可無限等待,也可等到一個(gè)指定的時(shí)間到達(dá)。插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
請(qǐng)仔細(xì)留意,處理者線程啟動(dòng)于創(chuàng)建者線程之前。如果以相反的順序啟動(dòng),將會(huì)在沒有處理者線程等待的情況下,添加第一條信息,此時(shí),沒有可供喚醒處理者線程,當(dāng)處理者線程運(yùn)行到它的第一個(gè)函數(shù)調(diào)用Wait時(shí),將會(huì)錯(cuò)過第一條信息,且只會(huì)在第二條信息存儲(chǔ)時(shí)被喚醒。

