NET多线程探索-线程同步和通信
NET中各种线程同步方法
在NET多线程开发中,有时候需要多个线程协调工作,完成这个步骤的过程称为“同步”。
使用同步的主要原因:
1.多个线程访问同一个共享资源。
2.多线程写入文件时保证只有一个线程使用文件资源。 3.由事件引发线程,线程等待事件,需要挂起线程。
NET中线程同步常见的几种方法:
1.lock
lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
lock的优点:简单易用,对象的同步几乎透明,轻量级。
使用lock需要注意:
锁定的对于应该是私有的,如果是公有的对象,可能出现超出控制范围的其它代码锁定该对象。
所以应该尽量避免使用lock(this),不保证会有其他线程闯入破坏数据正确性。
一个lock(this)错误的示例:
class Program {static void Main(string[] args){A a1 = new A();A a2 = new A();Thread T1 = new Thread(new ParameterizedThreadStart(a1.Test));Thread T2 = new Thread(new ParameterizedThreadStart(a2.Test));T1.Start(2);T2.Start(5);Console.Read();} } public class A {public static Int32 Count = 2;public void Test(object i){lock (this){Console.WriteLine("Count={0}", Count);Count += (Int32)i;Thread.Sleep(1 * 1000);Console.WriteLine("i={0},?Count+i={1}", i, Count);Console.WriteLine("--------------------------");}} }
这里数据和我们期待的不相同。
这里lock锁定的是A的实例,当线程T1执行的时候,lock锁定了a1对象,所以线程t2可以执行a2对象的Test方法。
正确的写法:
public class A {private static object obj = new object();public static Int32 Count = 2;public void Test(object i){lock (obj){Console.WriteLine("Count={0}", Count);Count += (Int32)i;Thread.Sleep(1 * 1000);Console.WriteLine("i={0},?Count+i={1}", i, Count);Console.WriteLine("--------------------------");}} }
这里的线程已经正常工作了,Count也正常的累加。
lock (obj)怎么错误了?
上面正常运行的程序稍微改动下!
class Program {static void Main(string[] args){A a1 = new A();A a2 = new A();Thread T1 = new Thread(new ParameterizedThreadStart(a1.Test));Thread T2 = new Thread(new ParameterizedThreadStart(a2.Test));T1.Start(2);T2.Start(5);Console.Read();} } public class A {private object obj = new object();public static Int32 Count = 2;public void Test(object i){lock (obj){Console.WriteLine("Count={0}", Count);Count += (Int32)i;Thread.Sleep(1 * 1000);Console.WriteLine("i={0},?Count+i={1}", i, Count);Console.WriteLine("--------------------------");}} }
分析下:这里我们把
private static object obj = new object();
中的static去掉了,所以obj变成了私有的实例成员,a1,a2都有不同的obj实例,所以lock这里也就没什么作用了!
让lock不再错下去!
这里我们也不再把static写上去了,只需要把调用那里稍微改动下。
class Program {static void Main(string[] args){A a1 = new A();Thread T1 = new Thread(new ParameterizedThreadStart(a1.Test));Thread T2 = new Thread(new ParameterizedThreadStart(a1.Test));T1.Start(2);T2.Start(5);Console.Read();} }
我们这里让a2对象去见马克思了,a1对象的Test调用了2次,这里lock锁定的obj都是a1的同一个私有对象obj,所以lock是起作用的!
Monitor
Monitor实现同步比lock复杂点,lock实际上是Monitor的简便方式,lock最终还是编译成Monitor。
不同处:
1.Monitor在使用的时候需要手动指定锁和手动释放手。
2.Monitor比lock多了几个实用的方法
public static bool Wait(object obj); public static void Pulse(object obj); public static void PulseAll(object obj);
Mointor同步实例:
class Program {static void Main(string[] args){Int32[] nums = { 1, 2, 3, 4, 5 };SumThread ST1 = new SumThread("Thread1");SumThread ST2 = new SumThread("Thread2");ST1.Run(nums);ST2.Run(nums);Console.Read();} }public class SumThread {//注意这里私有静态SA是SumThread共有的,所以考虑同步问题private static SumArray SA = new SumArray();private Thread t;public SumThread(string ThreadName){t = new Thread((object nums) =>{Console.WriteLine("线程{0}开始执行...", t.Name);Int32 i = SA.GetSum((Int32[])nums);Console.WriteLine("线程{0}执行完毕,sum={1}", t.Name, i);});t.Name = ThreadName;}public void Run(Int32[] nums){t.Start(nums);} }public class SumArray {private Int32 sum;private object obj = new object();public Int32 GetSum(Int32[] nums){Monitor.Enter(obj);try{//初始化sum值,以免获取到其它线程的脏数据sum = 0;foreach (Int32 num in nums){sum += num;Console.WriteLine("当前线程是:{0},sum={1}", Thread.CurrentThread.Name, sum);Thread.Sleep(1 * 1000);}return sum;}catch (Exception e){Console.WriteLine(e.Message);return 0;}finally{//很重要,千万不要忘记释放锁Monitor.Exit(obj);}} }
这里线程之间和蔼可亲的执行着,没有去抢着计算。
试着把
Monitor.Enter(obj); Monitor.Exit(obj);
去掉,那么线程就不会这么听话了!
另外一种同步的写法
前面使用的同步方式并不是所有情况都适合的,假如我们调用的是第三方的组件,我们没有修改源码的权限,那么我们只有考虑下面这种同步的实现。
class Program {static void Main(string[] args){Int32[] nums = { 1, 2, 3, 4, 5 };SumThread ST1 = new SumThread("Thread1");SumThread ST2 = new SumThread("Thread2");ST1.Run(nums);ST2.Run(nums);Console.Read();} }public class SumThread {//注意这里私有静态SA是SumThread共2有的,所以考虑同步问题private static SumArray SA = new SumArray();private Thread t;public SumThread(string ThreadName){t = new Thread((object nums) =>{Console.WriteLine("线程{0}开始执行...", t.Name);Monitor.Enter(SA);try{Int32 i = SA.GetSum((Int32[])nums);Console.WriteLine("线程{0}执行完毕,sum={1}", t.Name, i);}catch (Exception e){Console.WriteLine(e.Message);}finally{//很重要,千万不要忘记释放锁Monitor.Exit(SA);}});t.Name = ThreadName;}public void Run(Int32[] nums){t.Start(nums);} }public class SumArray {private Int32 sum;public Int32 GetSum(Int32[] nums){//初始化sum值,以免获取到其它线程的脏数据sum = 0;foreach (Int32 num in nums){sum += num;Console.WriteLine("当前线程是:{0},sum={1}", Thread.CurrentThread.Name, sum);Thread.Sleep(1 * 1000);}return sum;} }
注意:这里锁定的是SA.GetSum()调用本身,不是方法内部代码,SA对象是私有的。
Monitor-线程通信
当线程T正在lock块执行,需要访问另外一个线程lock块中的资源R,这个时候如果T等待R可用,有可能会让线程进入死循环。
这里就可以使用线程通信,先让T暂时放弃对lock块中的控制,等R变得可用,那么就通知线程T恢复运行。
public static bool Wait(object obj); public static void Pulse(object obj); public static void PulseAll(object obj);
上面就是这里需要用的方法,属于Monitor。 Wait是让线程暂停,这个方法有个重写,多了一个参数指定暂停的毫秒数。
Pulse是唤醒线程。
时钟滴答例子:
class Program {static void Main(string[] args){Clock C = new Clock();C.RunClock(1);Console.Read();} } public class Clock {private object obj = new object();//开始运行时钟,输入运行分钟public void RunClock(Int32 Minute){Thread T1 = new Thread((object Minute1) =>{Int32 m = Convert.ToInt32(Minute1) * 60 / 2;while (m > 0){DI(true);m--;}});Thread T2 = new Thread((object Minute1) =>{Int32 m = Convert.ToInt32(Minute1) * 60 / 2;while (m > 0){DA(true);m--;}});T1.Start(Minute);T2.Start(Minute);}public void DI(bool run){lock (obj){Console.WriteLine("嘀");Thread.Sleep(1000);Monitor.Pulse(obj);//执行完毕,唤醒其它线程Monitor.Wait(obj);//进入暂停,移交执行权利,等待唤醒}}public void DA(bool run){lock (obj){Console.WriteLine("嗒");Thread.Sleep(1000);Monitor.Pulse(obj);//执行完毕,唤醒其它线程Monitor.Wait(obj);//进入暂停,移交执行权利,等待唤醒}} }
谢谢Shikyoh指出错误,已经修改
就写到这里,下一篇写下互斥锁信号量这些。
作者:海不是蓝
博客:hailan2012
邮箱:hailan2012@sina.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载于:https://www.cnblogs.com/hailan2012/archive/2012/03/20/2408179.html
NET多线程探索-线程同步和通信相关推荐
- VC++ MFC 多线程及线程同步(详细、全面总结!)
更多详情:http://blog.csdn.net/whyacinth/ VC++ MFC 多线程及线程同步 关键词: MFC 多线程及线程同步 ...
- 3、Linux多线程,线程同步(转)
3.Linux多线程,线程同步 5)线程私有数据 进程内的所有线程共享进程的数据空间,因此全局变量为所有线程所共有.但有时线程也需要保存自己的私有数据,这时可以创建线程私有数据(Thread-spec ...
- 线程同步,通信与虚方法
线程同步,通信与虚方法 目录 线程同步,通信与虚方法 进程同步,通信 事件event 旗语semaphore 信箱mailbox 虚方法 实例理解 将子类句柄赋值成父类句柄 将父类句柄赋值成子类句柄 ...
- C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent
C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent 本章概要: 1:终止状态和非终止状态 2:AutoResetEvent和ManualResetE ...
- Java多线程之线程同步机制(锁,线程池等等)
Java多线程之线程同步机制 一.概念 1.并发 2.起因 3.缺点 二.三大不安全案例 1.样例一(模拟买票场景) 2.样例二(模拟取钱场景) 3.样例三(模拟集合) 三.同步方法及同步块 1.同步 ...
- Win32多线程编程(3) — 线程同步与通信
一.线程间数据通信 系统从进程的地址空间中分配内存给线程栈使用.新线程与创建它的线程在相同的进程上下文中运行.因此,新线程可以访问进程内核对象的所有句柄.进程中的所有内存以及同一个进程中其他所有线程的 ...
- 【Java 并发编程】多线程、线程同步、死锁、线程间通信(生产者消费者模型)、可重入锁、线程池
并发编程(Concurrent Programming) 进程(Process).线程(Thread).线程的串行 多线程 多线程的原理 多线程的优缺点 Java并发编程 默认线程 开启新线程 `Ru ...
- 多线程:线程同步与死锁(卖票案例)、线程通信、生产者与消费者
卖票案例 5个窗口同时卖票: 使用Runnable接口,只创建了一个ticket1对象,5个线程共享此对象,实现了资源共享. public class ticket1 implements Runna ...
- MFC 多线程及线程同步
一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常 ...
最新文章
- foxmail提示不知道这样的主机_不知道和婚礼策划师沟通时谈哪些?这样做让你高效备婚...
- phpstorm config include paths for swoole
- Ralink5350开发环境搭建
- Linux C 数据结构—-循环链表
- 前端学习(3009):vue+element今日头条管理--登录中的loding
- 免费mac虚拟机下载 快速安装win系统
- CUDA 开启GPU之间的P2P通信功能
- python print sys.stdout
- 微信支付 支付成功后不跳转 ecshop微信支付 如下操作即可
- 模拟集成电路学习心得(不见牛人,不懂世界之大!!!)
- javascript(jQuery版)切换tab效果自动切换(转自www.jqueryba.com)
- mongodb 高可用分布式原理 ---------搭建高可用mongo集群前需要温习的知识-火
- matlab软件topsis分析,基于AHP—TOPSIS的渗透测试工具的综合评价方法与流程
- 收藏|史上最全的30个生物实验技术及原理
- 【element】progress-修改进度条形状、高度、颜色、动态传值
- 算法笔记_110:第四届蓝桥杯软件类省赛真题(JAVA软件开发高职高专组部分习题)试题解答...
- 浅谈 Office 2013 App
- 汉字 字库压缩ttf
- android启动系统的图片裁剪工具
- 有点厉害!用12万行代码堆出来个蔡徐坤,关键是能跑能跳!