C#线程(四、简单的同步)
来源:互联网 发布:苹果手机怎么关4g网络 编辑:程序博客网 时间:2024/05/16 14:54
转自:见证大牛成长之路(http://blog.csdn.net/shanyongxu/article/details/47982419),本文为本人学习用,请点击链接查看原文,尊重楼楼大大版权。
简单的同步
看下面这一段代码:
class ThreadTest { static int _val1 = 1, _val2 = 1; internal static void Go() { if (_val2!=0) { Console.WriteLine(_val1/_val2); } _val2 = 0; } }分析:乍一看,没问题啊,但是我要告诉你,这段代码是费线程安全的,假设两个线程A和B,A和B都执行到了Go()方法的if判断中,假设_val2=1.所以两个线程都通过了了if判断,线程A执行了CW语句,然后退出了if语句,执行_val2=0,此时val2=0,但是此时线程B才刚刚执行到CW方法,而此时_val2=0.所以你有可能会得到一个divide by zero的异常.
so,为了保证线程安全,我们可以使用lock关键字,例如:
class ThreadTest { static readonly object _locker = new object(); static int _val1 = 1, _val2 = 1; internal static void Go() { lock (_locker) { if (_val2 != 0) { Console.WriteLine(_val1 / _val2); } _val2 = 0; } } }分析:此时两个线程A和B都只能有一个可以获得_locker锁,所以只能有一个线程来执行lock块的代码.
C#的lock关键字实际上是Moitor.Enter和Monitor.Exit的缩写.例如上面所写的代码和下面的等价:
Monitor.Enter(_locker); try { if (_val2 != 0) { Console.WriteLine(_val1 / _val2); } _val2 = 0; } finally { Monitor.Exit(_locker); }注意,如果在调用Monitor.Exit之前没有调用Monitor.Enter,则会抛出一个异常.
还有,在Monitor.Enter和try方法之间可能会抛出异常.例如在线程上调用Abort,或者是OutOfMemoryException.
为了解决这个问题,CLR 4.0提供了Monitor.Enter的重载,增加了lockTaken字段,当Monitor.Enter成果获取锁之后,lockTaken就是True,否则就为false.
我们同样可以将上面的代码写成下面这样:
class ThreadTest { static readonly object _locker = new object(); static int _val1 = 1, _val2 = 1; internal static void Go() { bool lockTaken = false; try { Monitor.Enter(_locker,ref lockTaken); if (_val2 != 0) { Console.WriteLine(_val1 / _val2); } _val2 = 0; } finally { if (lockTaken) { Monitor.Exit(_locker); } } } }Monitor也提供了TryEnter方法,并且可以传递一个超时时间.如果方法返回true,则代表获取了锁,否则为false.
选择同步对象
Monitor.Enter方法的参数是一个object类型,所以任何对象都可以是同步的,考虑下面的代码: int i=5; lock(i){} //锁定值类型 lock(this){} //锁定this对象 lock(typeof(Product)){}//锁定type对象 string str=”ddd”; lock(str){} //锁定字符串
锁定值类型会将值类型进行装箱,所以Monitor.Enter进入的是一个对象,但是Monitor.Exit()退出的是另一个不同的对象.
锁定this和type对象,会导致无法控制锁的逻辑,并且它很难保证不死锁和频繁的阻塞,在相同进程中锁定type对象会穿越应用程序域.
由于字符串驻留机制,所以也不要锁定string.
嵌套锁
同一个线程可以多次锁定同一对象.例如
lock(locker) lock(locker) lock(locker) { // do something }或者是
Monitor.Enter(locker); Monitor.Enter(locker); Monitor.Enter(locker); //code Monitor.Exit(locker); Monitor.Exit(locker); Monitor.Exit(locker);
static readonly object _locker = new object(); static void Main(string[] args) { lock (_locker) { Method(); } } static void Method() { lock (_locker) { //code } }
死锁
先看这样的代码
static readonly object locker1 = new object(); static readonly object locker2 = new object(); public static void MainThread() { new Thread(() => { lock (locker1) //获取锁locker1 { Thread.Sleep(1000); lock (locker2)//尝试获取locker2 { Console.WriteLine("locker1 locker2"); } } }).Start(); lock (locker2)//获取锁loccker2 { Thread.Sleep(1000);//尝试获取locker1 lock (locker1) { Console.WriteLine("locker2 locker1"); } } }分析:在这里,主线程先获取locker2的锁,然后sleep,接着尝试获取locker1的锁.副线程先获取locker1的锁,然后sleep,接着尝试获取locker2的锁.程序进入死锁状态,两个线程都在等待对方释放自己等待的锁.
CLR作为一个独立宿主环境,它不像SQL Server一样,他没有自动检测死锁机制,也不会结束一个线程来破坏死锁.死锁的线程会导致部分线程无限的等待.
小结一下
个人感觉那个死锁的案例,需要记住,记住代码。
再次声明:
转自:见证大牛成长之路(http://blog.csdn.net/shanyongxu/article/details/47982419),本文为本人学习用,请点击链接查看原文,尊重楼楼大大版权。
阅读全文
0 0
- C#线程(四、简单的同步)
- c#线程的同步
- C#的线程同步
- 简单讲述关于C#的线程,任务,同步
- 线程的同步(四)---信号量
- 四、线程的同步问题
- C#线程同步的总结
- C#的线程同步总结
- 线程同步的简单示例
- c#简单的多线程 同步
- 多线程编程之四 线程的同步
- C++线程同步的四种方式
- 线程同步的两种防护四
- C++ 线程同步的四种方式
- Windows线程同步的四种方法
- C#线程同步系列(四) 互斥对象Mutex
- 多线程(四)线程的同步之同步代码块
- 多线程(四)线程的同步之同步方法
- SDUT 3928 C~K玩游戏
- Android—布局
- 精通Dubbo——dubbo2.0源码中Spring Bean的加载
- 菜鸟小白的人生中第一篇博客
- POJ 1785 Binary Search Heap Construction 笔记
- C#线程(四、简单的同步)
- PL/SQL替换变量&实现根据输入信息进行查询
- JAVA 风雨学习路
- 简单的算法问题1——塑泥课
- DB2中SQL0668N Reason code "1"以及SQL3603N报错的处理
- A Diversity-Promoting Objective Function for Neural Conversation Models
- 五分钟教你Android-Kotlin项目编写
- 路线
- leetcode | 图片的旋转(顺时针90°) | Python