深入浅出多线程教程第五篇:探讨关键同步构造方法?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1038个文字,预计阅读时间需要5分钟。
1: Mutex + Mutex 就像两个锁在一起,就像一个 C++ 中的互斥锁。
1:Mutex
Mutex 就像一个C# lock一样,不同的是它可以跨进程.
进入和释放一个Mutex要花费几毫秒,大约比C#的lock慢50倍。
使用一个Mutex的实例,调用WaitOne方法来获取锁,ReleaseMutex方法来释放锁。
因为Mutex是跨进程的,所以我们可以使用Mutex来检测程序是否已经运行。
publicstaticvoidMainThread(){
using(varmutex=newMutex(false,"LoveJennyOneAtATimeDemo"))
{
if(!mutex.WaitOne(TimeSpan.FromSeconds(3),false))
{
Console.WriteLine("只能运行一个应用程序!");
return;
}
RunProgram();
}
}
2:Semaphore:
一个Semaphore就像一个酒吧一样,通过门卫来限制它的客人,一旦到达限制,没有人可以进入,
人们会在门外乖乖的排队,一旦有一个人离开酒吧,排队中的人就可以进入了一个了。
下面是个例子:
classTheClub{
//只能容纳三个人的酒吧
staticSemaphoreSlim_sem=newSemaphoreSlim(3);
publicstaticvoidMainThread()
{
for(inti=1;i<=5;i++)
newThread(Enter).Start(i);//有5个人向进入
}
staticvoidEnter(objectid)
{
Console.WriteLine(id+"想要进入了");
_sem.Wait();
Console.WriteLine(id+"已经进入了!");
Thread.Sleep(1000*(int)id);
Console.WriteLine(id+"离开了?");
_sem.Release();
}
}
3:AutoResetEvent
一个AutoResetEvent就像十字转门一样,插入一张票就让一个人通过,”Auto”代表门会自动的关上。
在十字门外面的人可以调用WaitOne方法来阻塞,等待。一旦有人插入了票(调用Set方法),就可以让外面等待的人(调用WaitOne方法的线程)通过了。
创建AutoResetEvent有一个参数。
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
其中false在msdn的解释是:初始状态为非终止,
按照我个人的理解false代表了十字转门非终止,所以可以正常的进入,等待。
而如果是true的话:初始状态为终止,也就是代表已经调用了Set了,
就是说十字转门已经停止了,所以接下来如果有人调用了WaitOne方法,这个调用WaitOne方法的人直接就可以进入了,不需要再插入票(不需要调用Set)了,之后的调用和false一致,这一点可以认为AutoResetEvent具有记忆功能,它记住了上次门是打开的状态。所以调用waitone方法可以进入。
classThreadAutoResetEvent{
staticEventWaitHandle_waitHandle=newAutoResetEvent(false);
publicstaticvoidMainThread()
{
newThread(Waiter).Start();
Thread.Sleep(2000);
_waitHandle.Set();
}
staticvoidWaiter()
{
Console.WriteLine("Waiting...");
_waitHandle.WaitOne();
Console.WriteLine("Notified");
}
}
很简单,Waiter执行到Waiting…后,就开始调用WaitOne了,所以在门外排队等待。
而主线程在睡了两秒后,开始插入一张票(Set).所以Waiter就继续执行,所以打印Notified
接下来我们使用AutoResetEvent来模拟实现生产消费问题:
{
EventWaitHandle_wh=newAutoResetEvent(false);
Thread_worker;
readonlyobject_locker=newobject();
Queue<string>_tasks=newQueue<string>();
publicProducerConsumerQueue()
{
//创建并启动工作线程
_worker=newThread(Work);
_worker.Start();
}
publicvoidEnqueueTask(stringtask)
{
lock(_locker)_tasks.Enqueue(task);
_wh.Set();//一旦有任务了,唤醒等待的线程
}
publicvoidDispose()
{
EnqueueTask(null);
_worker.Join();//等待_worker线程执行结束
_wh.Close();
}
voidWork()
{
while(true)
{
stringtask=null;
lock(_locker)
{
if(_tasks.Count>0)
{
task=_tasks.Dequeue();
if(task==null)
return;
}
if(task!=null)//如果有任务的话,执行任务
{
Console.WriteLine("Performingtask:"+task);
Thread.Sleep(1000);
}
else//否则阻塞,去睡觉吧
{
_wh.WaitOne();
}
}
}
}
}
主线程调用如下:
publicstaticvoidMain(){
using(ProducerConsumerQueueq=newProducerConsumerQueue())
{
q.EnqueueTask("Hello");
for(inti=0;i<10;i++)q.EnqueueTask("Say"+i);
q.EnqueueTask("Goodbye!");
}
}
4:ManualResetEvent:
一个ManualResetEvent就是一个普通门,
调用Set方法门就打开了,允许任意数量的人进入。
调用WaitOne方法就开始等待进入。
调用Reset方法门就关闭了。
在一个关闭的门上调用WaitOne方法就会被阻塞。
当门下次被打开的时候,所有等待的线程都可以进入了。
除了这些不同外,一个ManualResetEvent和AutoResetEvent类似。
在Framework4.0中ManualResetEvent提供了一个优化版本。ManualResetEventSlim。后面的版本速度更快,并且支持取消(CancellationToken).
参考资料:
www.albahari.com/threading/
CLR Via C# 3.0
本文共计1038个文字,预计阅读时间需要5分钟。
1: Mutex + Mutex 就像两个锁在一起,就像一个 C++ 中的互斥锁。
1:Mutex
Mutex 就像一个C# lock一样,不同的是它可以跨进程.
进入和释放一个Mutex要花费几毫秒,大约比C#的lock慢50倍。
使用一个Mutex的实例,调用WaitOne方法来获取锁,ReleaseMutex方法来释放锁。
因为Mutex是跨进程的,所以我们可以使用Mutex来检测程序是否已经运行。
publicstaticvoidMainThread(){
using(varmutex=newMutex(false,"LoveJennyOneAtATimeDemo"))
{
if(!mutex.WaitOne(TimeSpan.FromSeconds(3),false))
{
Console.WriteLine("只能运行一个应用程序!");
return;
}
RunProgram();
}
}
2:Semaphore:
一个Semaphore就像一个酒吧一样,通过门卫来限制它的客人,一旦到达限制,没有人可以进入,
人们会在门外乖乖的排队,一旦有一个人离开酒吧,排队中的人就可以进入了一个了。
下面是个例子:
classTheClub{
//只能容纳三个人的酒吧
staticSemaphoreSlim_sem=newSemaphoreSlim(3);
publicstaticvoidMainThread()
{
for(inti=1;i<=5;i++)
newThread(Enter).Start(i);//有5个人向进入
}
staticvoidEnter(objectid)
{
Console.WriteLine(id+"想要进入了");
_sem.Wait();
Console.WriteLine(id+"已经进入了!");
Thread.Sleep(1000*(int)id);
Console.WriteLine(id+"离开了?");
_sem.Release();
}
}
3:AutoResetEvent
一个AutoResetEvent就像十字转门一样,插入一张票就让一个人通过,”Auto”代表门会自动的关上。
在十字门外面的人可以调用WaitOne方法来阻塞,等待。一旦有人插入了票(调用Set方法),就可以让外面等待的人(调用WaitOne方法的线程)通过了。
创建AutoResetEvent有一个参数。
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
其中false在msdn的解释是:初始状态为非终止,
按照我个人的理解false代表了十字转门非终止,所以可以正常的进入,等待。
而如果是true的话:初始状态为终止,也就是代表已经调用了Set了,
就是说十字转门已经停止了,所以接下来如果有人调用了WaitOne方法,这个调用WaitOne方法的人直接就可以进入了,不需要再插入票(不需要调用Set)了,之后的调用和false一致,这一点可以认为AutoResetEvent具有记忆功能,它记住了上次门是打开的状态。所以调用waitone方法可以进入。
classThreadAutoResetEvent{
staticEventWaitHandle_waitHandle=newAutoResetEvent(false);
publicstaticvoidMainThread()
{
newThread(Waiter).Start();
Thread.Sleep(2000);
_waitHandle.Set();
}
staticvoidWaiter()
{
Console.WriteLine("Waiting...");
_waitHandle.WaitOne();
Console.WriteLine("Notified");
}
}
很简单,Waiter执行到Waiting…后,就开始调用WaitOne了,所以在门外排队等待。
而主线程在睡了两秒后,开始插入一张票(Set).所以Waiter就继续执行,所以打印Notified
接下来我们使用AutoResetEvent来模拟实现生产消费问题:
{
EventWaitHandle_wh=newAutoResetEvent(false);
Thread_worker;
readonlyobject_locker=newobject();
Queue<string>_tasks=newQueue<string>();
publicProducerConsumerQueue()
{
//创建并启动工作线程
_worker=newThread(Work);
_worker.Start();
}
publicvoidEnqueueTask(stringtask)
{
lock(_locker)_tasks.Enqueue(task);
_wh.Set();//一旦有任务了,唤醒等待的线程
}
publicvoidDispose()
{
EnqueueTask(null);
_worker.Join();//等待_worker线程执行结束
_wh.Close();
}
voidWork()
{
while(true)
{
stringtask=null;
lock(_locker)
{
if(_tasks.Count>0)
{
task=_tasks.Dequeue();
if(task==null)
return;
}
if(task!=null)//如果有任务的话,执行任务
{
Console.WriteLine("Performingtask:"+task);
Thread.Sleep(1000);
}
else//否则阻塞,去睡觉吧
{
_wh.WaitOne();
}
}
}
}
}
主线程调用如下:
publicstaticvoidMain(){
using(ProducerConsumerQueueq=newProducerConsumerQueue())
{
q.EnqueueTask("Hello");
for(inti=0;i<10;i++)q.EnqueueTask("Say"+i);
q.EnqueueTask("Goodbye!");
}
}
4:ManualResetEvent:
一个ManualResetEvent就是一个普通门,
调用Set方法门就打开了,允许任意数量的人进入。
调用WaitOne方法就开始等待进入。
调用Reset方法门就关闭了。
在一个关闭的门上调用WaitOne方法就会被阻塞。
当门下次被打开的时候,所有等待的线程都可以进入了。
除了这些不同外,一个ManualResetEvent和AutoResetEvent类似。
在Framework4.0中ManualResetEvent提供了一个优化版本。ManualResetEventSlim。后面的版本速度更快,并且支持取消(CancellationToken).
参考资料:
www.albahari.com/threading/
CLR Via C# 3.0

