C#线程同步的几种方法总结

时间:2021-05-20

我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在后台处理一大堆数据,但还要使用户界面处于可操作状态;或者你的程序需要访问一些外部资源如数据库或网络文件等。这些情况你都可以创建一个子线程去处理,然而,多线程不可避免地会带来一个问题,就是线程同步的问题。如果这个问题处理不好,我们就会得到一些非预期的结果。

在网上也看过一些关于线程同步的文章,其实线程同步有好几种方法,下面我就简单的做一下归纳。

一、volatile关键字

volatile是最简单的一种同步方法,当然简单是要付出代价的。它只能在变量一级做同步,volatile的含义就是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我。(【转自pilerServices 里面。但要注意这个属性会使整个方法加锁,直到方法返回,才释放锁。因此,使用上不太灵活。如果要提前释放锁,则应该使用Monitor或lock。我们来看一个例子:

[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 类都可以。

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方法,使状态由非终止变为终止,重新激活子线程。

以上就是小编给大家整理的全部相关知识点,感谢你的支持。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章