归纳一下:C#线程同步的几种方法 2

来源:互联网 发布:centos输入法下载安装 编辑:程序博客网 时间:2024/06/06 02:03


  八、MethodImplAttribute

  如果临界区是跨越整个方法的,也就是说,整个方法内部的代码都需要上锁的话,使用MethodImplAttribute属性会更简单一些。这样就不用在方法内部加锁了,只需要在方法上面加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了,MehthodImpl和MethodImplOptions都在命名空间System.Runtime.CompilerServices 里面。但要注意这个属性会使整个方法加锁,直到方法返回,才释放锁。因此,使用上不太灵活。如果要提前释放锁,则应该使用Monitor或lock。我们来看一个例子:


复制代码
  Code
[MethodImpl(MethodImplOptions.Synchronized)]
public void DoSomeWorkSync()
{
Console.WriteLine( " DoSomeWorkSync() -- Lock held by Thread " +
Thread.CurrentThread.GetHashCode());
Thread.Sleep( 1000 );
Console.WriteLine( " DoSomeWorkSync() -- Lock released by Thread " +
Thread.CurrentThread.GetHashCode());
}
public void DoSomeWorkNoSync()
{
Console.WriteLine( " DoSomeWorkNoSync() -- Entered Thread is " +
Thread.CurrentThread.GetHashCode());
Thread.Sleep( 1000 );
Console.WriteLine( " DoSomeWorkNoSync() -- Leaving Thread is " +
Thread.CurrentThread.GetHashCode());
}

[STAThread]
static void Main( string [] args)
{
MethodImplAttr testObj = new MethodImplAttr();
Thread t1 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync));
Thread t2 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync));
t1.Start();
t2.Start();
Thread t3 = new Thread( new ThreadStart(testObj.DoSomeWorkSync));
Thread t4 = new Thread( new ThreadStart(testObj.DoSomeWorkSync));
t3.Start();
t4.Start();

Console.ReadLine();

复制代码

这里,我们有两个方法,我们可以对比一下,一个是加了属性MethodImpl的DoSomeWorkSync(),一个是没加的DoSomeWorkNoSync()。在方法中Sleep(1000)是为了在第一个线程还在方法中时,第二个线程能够有足够的时间进来。对每个方法分别起了两个线程,我们先来看一下结果:

 

可以看出,对于线程1和2,也就是调用没有加属性的方法的线程,当线程2进入方法后,还没有离开,线程1有进来了,这就是说,方法没有同步。我们再来看看线程3和4,当线程3进来后,方法被锁,直到线程3释放了锁以后,线程4才进来。

  九、同步事件和等待句柄

  用lock和Monitor可以很好地起到线程同步的作用,但它们无法实现线程之间传递事件。如果要实现线程同步的同时,线程之间还要有交互,就要用到同步事件。同步事件是有两个状态(终止和非终止)的对象,它可以用来激活和挂起线程。

  同步事件有两种:AutoResetEvent和 ManualResetEvent。它们之间唯一不同的地方就是在激活线程之后,状态是否自动由终止变为非终止。AutoResetEvent自动变为非终止,就是说一个AutoResetEvent只能激活一个线程。而ManualResetEvent要等到它的Reset方法被调用,状态才变为非终止,在这之前,ManualResetEvent可以激活任意多个线程。

  可以调用WaitOne、WaitAny或WaitAll来使线程等待事件。它们之间的区别可以查看MSDN。当调用事件的 Set方法时,事件将变为终止状态,等待的线程被唤醒。

  来看一个例子,这个例子是MSDN上的。因为事件只用于一个线程的激活,所以使用 AutoResetEvent 或 ManualResetEvent 类都可以。


复制代码
Code
static AutoResetEvent autoEvent;

static void DoWork()
{
Console.WriteLine(" worker thread started, now waiting on event");
autoEvent.WaitOne();
Console.WriteLine(" worker thread reactivated, now exiting");
}

[STAThread]
static void Main(string[] args)
{
autoEvent = new AutoResetEvent(false);

Console.WriteLine("main thread starting worker thread");
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();

Console.WriteLine("main thrad sleeping for 1 second");
Thread.Sleep(1000);

Console.WriteLine("main thread signaling worker thread");
autoEvent.Set();

Console.ReadLine();

复制代码

我们先来看一下输出:


在主函数中,首先创建一个AutoResetEvent的实例,参数false表示初始状态为非终止,如果是true的话,初始状态则为终止。然后创建并启动一个子线程,在子线程中,通过调用AutoResetEvent的WaitOne方法,使子线程等待指定事件的发生。然后主线程等待一秒后,调用AutoResetEvent的Set方法,使状态由非终止变为终止,重新激活子线程。

参考:

1/MSDN(http://msdn.microsoft.com/zh-cn/library/ms173179(VS.80).aspx )

2/http://www.cnblogs.com/VincentWP/archive/2008/06/25/1229104.html



分类: 02 C#/.NET


好文要顶 关注我 收藏该文  


 
loose_went
关注 - 1
粉丝 - 230


+加关注


37

0


(请您对文章做出评价)


?  上一篇:测试一下你对IP地址的掌握水平(网管面试时会用到)
?  下一篇:[转]应用软件系统架构设计的“七种武器”


posted @ 2008-09-20 17:10 loose_went 阅读(80037) 评论(37) 编辑 收藏
 


评论列表



  
#1楼 2008-09-20 05:00 谢里斯  

蛮完整的,总结的不错。

如果需要根据变量来锁定资料的话,除了 Mutex 还可以使用什么呢?

支持(0)反对(0)


  
#2楼 2008-09-20 08:02 devil0153  

顶一个,总结的不错,我原来只知道一两个,学习了:)

支持(0)反对(0)
 


  
#3楼 2008-09-20 08:38 Q.Lee.lulu  

非常不错!!!

支持(0)反对(0)
 


  
#4楼 2008-09-20 09:00 Jianqiang Bao  

如何避免死锁???

支持(0)反对(0)
 


  
#5楼 2008-09-20 09:58 有容乃大  

总结得不错,收藏。
我一般简单的使用lock就行了。

-----------------------------------------------
发布.net项目开发工具(V3.0 ):
http://www.cnblogs.com/mrhgw/archive/2008/08/06/1261664.html
http://www.mrhgw.cn

支持(0)反对(0)
 


  
#6楼 2008-09-20 10:16 Kevin Dai  

Jeffrey Richter的那本《CLR Via C#》上面也总结的相当不错……

支持(0)反对(0)
 


  
#7楼 2008-09-20 14:56 gussing  

good job!

支持(0)反对(0)
 


  
#8楼 2008-09-20 15:42 Artech  

挺好!

支持(0)反对(0)
 


  
#9楼 2008-09-20 17:29 子逸  

帖子很好, 谢谢分享
只是有一点比较奇怪:
为什么2008-09-20 17:10 发的帖子
10条回帖竟然都是 17:10 之前的?

支持(0)反对(0)
 


  
#10楼[楼主] 2008-09-20 17:29 loose_went  

@包建强
个人觉得,要想避免死锁,需要保证被锁的对象不能够在任意地方都可以锁它。最好是将其声明为私有或保护成员。还有锁定的临界区代码要尽可能的短比较好。不知道大家还有什么高见?

支持(0)反对(0)
 


  
#11楼[楼主] 2008-09-20 17:30 loose_went  

@子逸

呵呵,因为这篇文章我一直在修改,第一次发表的时间是2008-09-20 03:50 。这么长的文章,怕有什么地方写的不准确,所以一直在看。

支持(1)反对(0)
 


  
#12楼 2008-09-20 18:05 Jianqiang Bao  

ReaderWriterLock是避免死锁的一种方法,线程池也是一种,异步回调其实也是,还有就是双缓冲技术。巧妙利用Monitor.Pulse和Monitor.Wait也是一种好的解决方案。

支持(0)反对(0)
 


  
#13楼 2008-09-20 18:27 DreamTrue  

好东西

支持(0)反对(0)
 


  
#14楼 2008-09-20 23:49 Jimmy Zhang  

很不错

支持(0)反对(0)
 


  
#15楼 2008-09-21 11:17 Angel Lucifer  

@包建强
@loose_went

只要使用锁,避免死锁就是一项艰巨的任务。

ReaderWriterLock,线程池等仅仅提高了避免死锁的可能,并不能从根本上解决问题。而且现有的 .NET ThreadPool 还有死锁的 Bug 未消除。

.NET ReaderWriterLock 性能太低,不堪重用。Microsoft 自己都建议使用 ReaderWriterLockSlim 来代替。

此外文中的二,五,七,八本质上说的都是一种同步机制,七,八根本就不推荐使用。同步事件虽然是 Windows 平台多线程开发人员最常用的机制,可惜也是隐患最多的同步机制。C++ 的 Boost 库甚至都不提供事件机制。条件变量是更好的替代机制(Vista 已经原生支持,我们也可以使用 Semaphore 和 Event 来模拟)。

避免死锁并没有银弹,仍然要看开发人员对并行程序设计的水平如何。更重要的是,除此之外,还要避免活锁。

支持(0)反对(0)


  
#16楼 2008-09-21 13:09 Anders Cui  

不错,感谢分享!

支持(0)反对(0)
 


  
#17楼 2008-09-23 16:34 zklvy

单态模式是不是也是一种选择?

支持(0)反对(0)


  
#18楼 2008-09-29 10:41 ppchen(陈荣林)  

这么多!只知道其


0 0
原创粉丝点击