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多线程探索-线程同步和通信相关推荐

  1. VC++ MFC 多线程及线程同步(详细、全面总结!)

    更多详情:http://blog.csdn.net/whyacinth/ VC++ MFC 多线程及线程同步 关键词: MFC    多线程及线程同步                          ...

  2. 3、Linux多线程,线程同步(转)

    3.Linux多线程,线程同步 5)线程私有数据 进程内的所有线程共享进程的数据空间,因此全局变量为所有线程所共有.但有时线程也需要保存自己的私有数据,这时可以创建线程私有数据(Thread-spec ...

  3. 线程同步,通信与虚方法

    线程同步,通信与虚方法 目录 线程同步,通信与虚方法 进程同步,通信 事件event 旗语semaphore 信箱mailbox 虚方法 实例理解 将子类句柄赋值成父类句柄 将父类句柄赋值成子类句柄 ...

  4. C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent

    C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent 本章概要: 1:终止状态和非终止状态 2:AutoResetEvent和ManualResetE ...

  5. Java多线程之线程同步机制(锁,线程池等等)

    Java多线程之线程同步机制 一.概念 1.并发 2.起因 3.缺点 二.三大不安全案例 1.样例一(模拟买票场景) 2.样例二(模拟取钱场景) 3.样例三(模拟集合) 三.同步方法及同步块 1.同步 ...

  6. Win32多线程编程(3) — 线程同步与通信

    一.线程间数据通信 系统从进程的地址空间中分配内存给线程栈使用.新线程与创建它的线程在相同的进程上下文中运行.因此,新线程可以访问进程内核对象的所有句柄.进程中的所有内存以及同一个进程中其他所有线程的 ...

  7. 【Java 并发编程】多线程、线程同步、死锁、线程间通信(生产者消费者模型)、可重入锁、线程池

    并发编程(Concurrent Programming) 进程(Process).线程(Thread).线程的串行 多线程 多线程的原理 多线程的优缺点 Java并发编程 默认线程 开启新线程 `Ru ...

  8. 多线程:线程同步与死锁(卖票案例)、线程通信、生产者与消费者

    卖票案例 5个窗口同时卖票: 使用Runnable接口,只创建了一个ticket1对象,5个线程共享此对象,实现了资源共享. public class ticket1 implements Runna ...

  9. MFC 多线程及线程同步

    一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常 ...

最新文章

  1. foxmail提示不知道这样的主机_不知道和婚礼策划师沟通时谈哪些?这样做让你高效备婚...
  2. phpstorm config include paths for swoole
  3. Ralink5350开发环境搭建
  4. Linux C 数据结构—-循环链表
  5. 前端学习(3009):vue+element今日头条管理--登录中的loding
  6. 免费mac虚拟机下载 快速安装win系统
  7. CUDA 开启GPU之间的P2P通信功能
  8. python print sys.stdout
  9. 微信支付 支付成功后不跳转 ecshop微信支付 如下操作即可
  10. 模拟集成电路学习心得(不见牛人,不懂世界之大!!!)
  11. javascript(jQuery版)切换tab效果自动切换(转自www.jqueryba.com)
  12. mongodb 高可用分布式原理 ---------搭建高可用mongo集群前需要温习的知识-火
  13. matlab软件topsis分析,基于AHP—TOPSIS的渗透测试工具的综合评价方法与流程
  14. 收藏|史上最全的30个生物实验技术及原理
  15. 【element】progress-修改进度条形状、高度、颜色、动态传值
  16. 算法笔记_110:第四届蓝桥杯软件类省赛真题(JAVA软件开发高职高专组部分习题)试题解答...
  17. 浅谈 Office 2013 App
  18. 汉字 字库压缩ttf
  19. android启动系统的图片裁剪工具
  20. 有点厉害!用12万行代码堆出来个蔡徐坤,关键是能跑能跳!

热门文章

  1. Web API 接口
  2. 通过Java代码浅谈HTTP协议
  3. 安全使用网上银行 享受在线购物时尚生活
  4. C# webservice调用方法总结
  5. 我30岁了。现在开始编程,会不会太晚?
  6. Altium Designer之PCB
  7. S5PV210的内存映射
  8. 看完这篇还不懂 MySQL 主从复制,可以回家躺平了~
  9. 一款零注解API接口文档生成工具
  10. 那些和闰年相关的 Bug