您的位置:首页 > 编程语言 > C#

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

2016-06-06 11:32 459 查看
  八、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(陈荣林)  

这么多!只知道其
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: