c# thread4——lock,死锁,以及monitor关键字
多线程的存在是提高系统效率,挖掘cpu性能的一种手段,那么控制它,能够协同多个线程不发生bug是关键。
首先我们来看一段不安全的多线程代码。
public abstract class CalculateBase
{
public int count = ;
public object _lock = new object();
public abstract void Operation();
} public class NoSafeCalculate : CalculateBase
{
public override void Operation()
{
count++;
count--;
}
}
static void Main(string[] args)
{
CalculateBase calculate = new NoSafeCalculate();
Thread thread1 = new Thread(() =>
{
for (int i = ; i < ; i++)
{
calculate.Operation();
}
});
Thread thread2 = new Thread(() =>
{
for (int i = ; i < ; i++)
{
calculate.Operation();
}
});
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Console.WriteLine(calculate.count);
Console.ReadLine(); }
很简单的一段代码,大致意思就是启动两个线程去加减一个变量,由于是一个实例化对象,所以变量也是同一个,按道理来说,变量自增一次自减一次,应该是不变的,在单线程中确实如此,但是在多线程中结果就会发生变化。
我们来执行一下。
发现结果并不是0,而是226.并且每次执行都是不一样的结果。
为什么会出现这种情况?
相信很多人都知道
1.++,--不是原子操作,举个例子a++其实是多步操作。1)计算a+1的值。2)将值赋值给a,那么多线程在执行这种操作的时候就很容易引起并发问题,结果不一致。(c#自带原子性操作函数)
2.值的不一致。比如我这边的count在执行++操作,但是还没有执行完,另外一个线程也执行到了++操作,结果两个++执行完之后,最终只是+了一次。
要解决这种多线程并发问题,如果不考虑性能,那么lock关键字将是很好的选择
我们来看看安全的代码
public abstract class CalculateBase
{
public int count = ;
public object _lock = new object();
public abstract void Operation();
} public class NoSafeCalculate : CalculateBase
{
public override void Operation()
{
count++;
count--;
}
}
public class SafeCalculate : CalculateBase
{
public override void Operation()
{
lock (_lock)
{
count++;
count--;
}
}
}
static void Main(string[] args)
{
CalculateBase calculate = new SafeCalculate();
Thread thread1 = new Thread(() =>
{
for (int i = ; i < ; i++)
{
calculate.Operation();
}
});
Thread thread2 = new Thread(() =>
{
for (int i = ; i < ; i++)
{
calculate.Operation();
}
});
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Console.WriteLine(calculate.count);
Console.ReadLine(); }
很显然,相较于不安全的代码我们只是多了一个lock的作用域,我们在Operation方法中添加了一个lock作用域将我们的自增和自减包裹起来,并且传入了一个object类型的_lock参数。
结果:0
多次测试结果都是0,那么可以说线城是安全了,那么lock关键字的作用是什么。
c#中lock关键字等同于java中的synchionzed,意思为"同步",也就是说被它包裹的代码段都将以同步的方式进行,也就是同时以单线程执行。
那么它是如何做到的?其实就是传入的参数的作用了,我们看到传入了一个_lock参数,其实它就是一个"锁的钥匙",谁能拿到他谁就能打开锁,执行代码。所以每次有一个线程拿到锁后,其他的线程只能等待这个线程执行完然后将锁释放,其他线程才能够继续执行。
(附加:为什么用object类型作为锁的钥匙,string或者基本类型可以么?有什么区别)
何为死锁
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源会互相等待对方释放资源,导致这些线程处于等待状态,无法前往执行。如果线程都不主动释放所占有的资源,将产生死锁。
我们来完成一个死锁的demo
public class DeadLock
{
public object _lock = new object();
public object _lock1 = new object();
public void Into()
{
lock (_lock)
{
Thread.Sleep();
lock (_lock1)
{
Console.WriteLine("I am success come in");
}
}
}
public void Out()
{
lock (_lock1)
{
Thread.Sleep();
lock (_lock)
{
Console.WriteLine("I am success go out");
}
}
}
}
static void Main(string[] args)
{
DeadLock dead = new DeadLock();
Thread thread1 = new Thread(() => { dead.Into(); });
Thread thread2 = new Thread(() => { dead.Out(); });
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Console.WriteLine("执行结束");
}
运行代码:
那基本上是这辈子看不到打印"执行结束"这四个字了。
其实上面的例子就是一个死锁的典型场景,一个线程拿住了A钥匙,打开了A锁,执行完后需要B钥匙,才能打开B锁,可是B钥匙永远也拿不到,因为另外一个线程正占用这B钥匙,在等待A锁的释放
那么怎么解决这种问题呢。
1.尽量用不同的对象作为锁的"钥匙"
2.使用c#提供的monitor关键字
我们将上面的代码进行修改。
public class DeadLock
{
public object _lock = new object();
public object _lock1 = new object();
public void Into()
{
lock (_lock)
{
Thread.Sleep();
lock (_lock1)
{
Console.WriteLine("I am success come in");
}
}
}
public void Out()
{
lock (_lock1)
{
Thread.Sleep();
if (Monitor.TryEnter(_lock, ))
{
Console.WriteLine("go out success");
}
else
{
Console.WriteLine("timeout");
} }
}
}
此时我们再来看看运行结果:
可以看到我们的程序死锁走出来了。
我们的monitor的作用就是会接受一个超时的参数,如果在时间内拿到锁,则返回true否则返回false,避免了死锁
最后补充下,其实lock也是Monitor的一个语法糖,分解lock的代码我们就能够了解。
public void demoLock()
{
bool getlock = false;
try
{
Monitor.Enter(_lock, ref getlock);
}
finally
{
if (getlock)//如果拿到锁则释放锁
{
//业务
//...
Monitor.Exit(_lock);
}
}
}
c# thread4——lock,死锁,以及monitor关键字的更多相关文章
- C#中lock死锁实例教程
这篇文章主要介绍了C#中lock死锁的用法,对于共享资源的访问及C#程序设计的安全性而言,有着非常重要的意义!需要的朋友可以参考下 链接:http://www.jb51.net/article/543 ...
- INSERT ... ON DUPLICATE KEY UPDATE产生death lock死锁原理
前言 编辑 我们在实际业务场景中,经常会有一个这样的需求,插入某条记录,如果已经存在了则更新它如果更新日期或者某些列上的累加操作等,我们肯定会想到使用INSERT ... ON DUPLICATE K ...
- C# 线程锁Lock 死锁
使用lock场景 多线程环境中,不使用lock锁,会形成竞争条件,导致错误. 使用lock 锁 可以保证当有线程操作某个共享资源时,其他线程必须等待直到当前线程完成操作. 即是多线程环境,如果一个线程 ...
- 使用显式的Lock对象取代synchronized关键字进行同步
Java SE5的java.util.concurrent类库还包含有定义在java.util.concurrent.locks中的显式的互斥机制.Lock对象必须被显式地创建.锁定和释放.因此,它与 ...
- C# lock 死锁问题排查方法
多线程程序发生死锁,某些重要线程卡住,不正常工作.排查起来非常麻烦.以下内容记录排查方法 1.确定死锁的位置,一般死锁会lock到某一行具体的代码,比如我就死锁在类似如下代码中 public void ...
- 简单的Lock死锁例子
static void Main(string[] args) { lock (_lock1) { var t = new Thread(() => { lock (_lock1) { Cons ...
- MySQL redo lock 死锁问题排查 & 解决过程
版权声明:本文由张青林原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/181 来源:腾云阁 https://www.qclo ...
- 使用lock锁或Monitor.Enter的目的
锁定的目的:由于多个线程 并行/并发 处理同一个“数据对象”(比如:在其它线程的某个地方发生了Clear.Add.Remove.Change等操作),导致“数据对象”不断变化,没法用了,所以,为了保证 ...
- C#中Monitor类、Lock关键字和Mutex类
线程:线程是进程的独立执行单元,每一个进程都有一个主线程,除了主线程可以包含其他的线程.多线程的意义:多线程有助于改善程序的总体响应性,提高CPU的效率.多线程的应用程序域是相当不稳定的,因为多个线程 ...
随机推荐
- Vue Google浏览器插件 Vue Devtools无法使用的解决办法
1.插件安装不必多说 一定要用Vue.js 开发版 Vue.min.js 在控制面板就不会显示 2.本地调试 用的是file://协议 修改插件允许访问文件网址 打上对勾
- thinkphp5 select对象怎么转数组?
DB操作返回是数组.模型直接操作返回是对象 对象类型转换数组打开 database.php 增加或修改参数'resultset_type' => '\think\Collection',即可连贯 ...
- MapReduce单机提交(待稿)
MR 提交方式源码 提交方式: 1,开发-> jar -> 上传到集群中的某一个节点 -> hadoop jar ooxx.jar ooxx in out 2,嵌入[linux,wi ...
- ubuntu(linux)如何安装nginx?
之前要在linux下面安装nginx,弄了半天,终于搞定了,下面给大家详细一下安装流程及安装报错解决方案: 安装共分为5步搞定: 1.进入src目录(下载存放目录) cd /usr/loca ...
- ThinkPHP生成静态页buildHtml方法
原来ThinkPHP自带了生成静态页的函数buildHtml,使用起来很方便!最新的手册里没写这个方法,向大家介绍一下. PHP 1 2 3 4 5 6 7 8 9 10 11 protect ...
- HTML5 入门基础
HTML5概述HTML5於2004年被WHATWG(网页超文本技术工作小组)提出,於2007年被W3C接纳.在2008年1月22日,第一份正式草案已公布.WHATWG表示该规范是目前正在进行的工作,仍 ...
- DevExpress v18.2版本亮点——Office File API 篇
行业领先的.NET界面控件——DevExpress v18.2版本亮点详解,本文将介绍了DevExpress Office File API v18.2 的版本亮点,新版30天免费试用!点击下载> ...
- Django【第2篇】:Django之反向解析
Django框架之第二篇 一.知识点回顾 1.MTV模型 model:模型,和数据库相关的 template:模板,存放html文件,模板语法(目的是将变量如何巧妙的嵌入到HTML页面中). view ...
- [REPRINT]Properties vs. Getters and Setters
http://www.python-course.eu/python3_properties.php Our new class means breaking the interface. The a ...
- LeetCode--114--二叉树展开为链表(python)
给定一个二叉树,原地将它展开为链表. 例如,给定二叉树 1 / \ 2 5 / \ \ 3 4 6将其展开为: 1 \ 2 \ 3 \ 4 \ ...