[计算机]在Net框架中C实现多线程的同步方法详解

上传人:豆浆 文档编号:33375796 上传时间:2018-02-15 格式:DOC 页数:11 大小:62KB
返回 下载 相关 举报
[计算机]在Net框架中C实现多线程的同步方法详解_第1页
第1页 / 共11页
[计算机]在Net框架中C实现多线程的同步方法详解_第2页
第2页 / 共11页
[计算机]在Net框架中C实现多线程的同步方法详解_第3页
第3页 / 共11页
[计算机]在Net框架中C实现多线程的同步方法详解_第4页
第4页 / 共11页
[计算机]在Net框架中C实现多线程的同步方法详解_第5页
第5页 / 共11页
点击查看更多>>
资源描述

《[计算机]在Net框架中C实现多线程的同步方法详解》由会员分享,可在线阅读,更多相关《[计算机]在Net框架中C实现多线程的同步方法详解(11页珍藏版)》请在金锄头文库上搜索。

1、在.Net 框架中 C#实现多线程的同步方法详解本文主要描述在 C#中线程同步的方法。线程的基本概念网上资料也很多就不再赘述了。直接接入主题,在多线程开发的应用中,线程同步是不可避免的。在.Net 框架中,实现线程同步主要通过以下的几种方式来实现,在 MSDN 的线程指南中已经讲了几种,本文结合作者实际中用到的方式一起说明一下。1. 维护自由锁(InterLocked) 实现同步2. 监视器(Monitor )和互斥锁( lock)3. 读写锁(ReadWriteLock)4. 系统内核对象1) 互斥(Mutex), 信号量(Semaphore), 事件(AutoResetEvent/Manu

2、alResetEvent)2) 线程池除了以上的这些对象之外实现线程同步的还可以使用 Thread.Join 方法。这种方法比较简单,当你在第一个线程运行时想等待第二个线程执行结果,那么你可以让第二个线程 Join进来就可以了。自由锁(InterLocked)对一个 32 位的整型数进行递增和递减操作来实现锁,有人会问为什么不用+ 或-来操作。因为在多线程中对锁进行操作必须是原子的,而+和- 不具备这个能力。InterLocked类还提供了两个另外的函数 Exchange, CompareExchange 用于实现交换和比较交换。Exchange 操作会将新值设置到变量中并返回变量的原来值:

3、int oVal = InterLocked.Exchange(ref val, 1)。监视器(Monitor)在 MSDN 中对 Monitor 的描述是: Monitor 类通过向单个线程授予对象锁来控制对对象的访问。Monitor 类是一个静态类因此你不能通过实例化来得到类的对象。Monitor 的成员可以查看 MSDN,基本上 Monitor 的效果和 lock 是一样的,通过加锁操作 Enter 设置临界区,完成操作后使用 Exit 操作来释放对象锁。不过相对来说 Monitor 的功能更强,Moniter可以进行测试锁的状态,因此你可以控制对临界区的访问选择,等待 or 离开, 而

4、且Monitor 还可以在释放锁之前通知指定的对象,更重要的是使用 Monitor 可以跨越方法来操作。Monitor 提供的方法很少就只有获取锁的方法 Enter, TryEnter;释放锁的方法Wait, Exit;还有消息通知方法 Pulse, PulseAll。经典的 Monitor 操作是这样的:/ 通监视器来创建临界区 static public void DelUser(string name)try / 等待线程进入 Monitor.Enter(Names);Names.Remove(name);Console.WriteLine(Del: 0, Names.Count);Mo

5、nitor.Pulse(Names);finally/ 释放对象锁 Monitor.Exit(Names); 其中 Names 是一个 List, 这里有一个小技巧,如果你想声明整个方法为线程同步可以使用方法属性:/ 通过属性设置整个方法为临界区 MethodImpl(MethodImplOptions.Synchronized) static public void AddUser(string name) Names.Add(name); Console.WriteLine(Add: 0,Names.Count); 对于 Monitor 的使用有一个方法是比较诡异的,那就是 Wait 方法

6、。在 MSDN 中对 Wait的描述是: 释放对象上的锁以便允许其他线程锁定和访问该对象。这里提到的是先释放锁,那么显然我们需要先得到锁,否则调用 Wait 会出现异常,所以我们必须在 Wait 前面调用 Enter 方法或其他获取锁的方法,如 lock,这点很重要。对应Enter 方法, Monitor 给出来另一种实现 TryEnter。这两种方法的主要区别在于是否阻塞当前线程,Enter 方法在获取不到锁时,会阻塞当前线程直到得到锁。不过缺点是如果永远得不到锁那么程序就会进入死锁状态。我们可以采用 Wait 来解决,在调用 Wait 时加入超时时限就可以。if (Monitor.TryE

7、nter(Names) Monitor.Wait(Names, 1000); / ! Names.Remove(name); Console.WriteLine(Del: 0, Names.Count);Monitor.Pulse(Names); 互斥锁(lock)lock 关键字是实现线程同步的比较简单的方式,其实就是设置一个临界区。在 lock 之后的.区块为一个临界区,当进入临界区时加互斥锁,离开临界区时释放互斥锁。MSDN对 lock 关键字的描述是: lock 关键字可将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。具体例子如下:static public

8、void ThreadFunc(object name)string str = name as string;Random rand = new Random();int count = rand.Next(100, 200);for (int i = 0; i AutoResetEvent一个 AutoResetEvent 象是一个 检票轮盘:插入一张通行证然后让一个人通过。auto的意思就是这个轮盘自动关闭或者打开让某人通过。线程将在调用 WaitOne 后进行等待或者是阻塞,并且通过调用 Set 操作来插入线程。如果一堆线程调用了 WaitOne 操作,那么轮盘就会建立一个等待队列。一

9、个通行证可以来自任意一个线程,换句话说任意一个线程都可以通过访问 AutoResetEvent 对象并调用 Set 来释放一个阻塞的线程。如果在 Set 被调用的时候没有线程等待,那么句柄就会一直处于打开状态直到有线程调用了 WaitOne 操作。这种行为避免了竞争条件-当一个线程还没来得急释放而另一个线程就开始进入的情况。因此重复的调用 Set 操作一个轮盘 哪怕是没有等待线程也不会一次性的让所有线程进入。WaitOne 操作接受一个超时参数-当发生等待超时的时候,这个方法会返回一个 false。当已有一个线程在等待的时候,WaitOne 操作可以指定等待还是退出当前同步上下文。Reset

10、操作提供了关闭轮盘的操作。AutoResetEvent 能够通过两个方法来创建: 1.调用构造函数 EventWaitHandle wh = new AutoResetEvent (false); 如果 boolean值为 true,那么句柄的 Set 操作将在创建后自动被调用 ;2. 通过基类EventWaitHandle 方式 EventWaitHandle wh = new EventWaitHandle (false, EventResetMode.Auto); EventWaitHandle 构造函数允许创建一个ManualResetEvent。人们应该通过调用 Close 来释放一

11、个 Wait Handle 在它不再使用的时候。当在应用程序的生存期内 Wait handle 继续被使用,那么如果遗漏了 Close 这步,在应用程序关闭的时候也会被自动释放。class BasicWaitHandle static EventWaitHandle wh = new AutoResetEvent(false);static void Main()new Thread(Waiter).Start();Thread.Sleep(1000); / 等待一会儿 wh.Set(); / 唤醒 static void Waiter()Console.WriteLine(Waiting.)

12、;wh.WaitOne(); / 等待唤醒 Console.WriteLine(Notified); ManualResetEventManualResetEvent 是 AutoResetEvent 的一个特例。它的不同之处在于在线程调用WaitOne 后不会自动的重置状态。它的工作机制有点象是开关:调用 Set 打开并允许其他线程进行 WaitOne;调用 Reset 关闭那么排队的线程就要等待,直到下一次打开。可以使用一个带 volatile 声明的 boolean 字段来模拟间断休眠 - 通过重复检测标志,然后休眠一小段时间。ManualResetEvent 常常被用于协助完成一个特殊

13、的操作,或者让一个线程在开始工作前完成初始化。线程池(Thread Pooling)如果你的应用程序拥有大量的线程并花费大量的时间阻塞在一个 Wait Handle 上,那么你要考虑使用线程池(Thead pooling)来处理。线程池通过合并多个 Wait Handle 来节约等待的时间。当 Wait Handle 被激活时,使用线程池你需要注册一个 Wait Handle 到一个委托去执行。通过调用 ThreadPool.RegisterWaitForSingleObject 方法:class Test static ManualResetEvent starter = new Manua

14、lResetEvent(false); public static void Main()ThreadPool.RegisterWaitForSingleObject(starter, Go, hello, -1, true);Thread.Sleep(5000);Console.WriteLine(Signaling worker.);starter.Set(); Console.ReadLine();public static void Go(object data, bool timedOut) Console.WriteLine(Started + data); / Perform t

15、ask. 对于 Wait Handle 和委托, RegisterWaitForSingleObject 接受一个黑盒对象并传递给你的委托(就像 ParameterizedThreadStart),超时设置和 boolean 标志指示了关闭和循环的请求。所有进入池中的线程都被认为是后台线程,这就意味着它们不再由应用程序控制,而是由系统控制直到应用程序退出。注意:如果这时候调用 Abort 操作,可能会发生意想不到的情况。你也可以通过调用 QueueUserWorkItem 方法使用线程池,指定委托并立即被执行。这时你不能在多任务情况下保存共享线程,但是可以得到另外的好处:线程池会保持一个线程的

16、总容量,当作业数超出容量时自动插入任务。class Test static object workerLocker = new object();static int runningWorkers = 100;public static void Main() for (int i = 0; i 0) Monitor.Wait(workerLocker);Console.WriteLine(Complete!);Console.ReadLine(); public static void Go(object instance) Console.WriteLine(Started: + instance);Thread.Sleep(1000); Console.WriteL

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号