C# 多线程系列(六)
2024-10-01 00:06:15
同步
当多个线程共享一些数据的时候,我们就需要使用同步技术,确保一次只有一个线程访问合改变共享状态。注意,同步问题与争用和死锁有关。
例:
static int idx = ;
static void Add()
{
for (int i = ; i < ; i++)
{
idx++;
}
}
static void Main()
{
const int SIZE = ;
Task[] arr = new Task[SIZE];
while (true)
{
for (int i = ; i < SIZE; i++)
{
arr[i] = new Task(Add);
arr[i].Start(); //启动多个线程
} for (int i = ; i < SIZE; i++)
{
arr[i].Wait(); //等待线程完成
} Console.WriteLine(idx);
Thread.Sleep();
idx = ;// 重置数据,再次运行
}
}
结果:
1717634
1652989
1444839
1272385
1558097
1297459
1968232
2000000
显然,不是我们想要的,我们期望每次运行的结果都是2000000。这是因为idx++不是线程安全的,它的操作包括从内存中获取一个值,给该值递增1,再将它存回内存。这些操作都可能会被线程调度器打断。
这种情况下,我们就需要一些同步方法解决该问题。
- lock关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。在块的开始处调用 Enter,而在块的结尾处调用 Exit。这样可确保当一个线程位于代码的关键部分时,另一个线程不会进入该关键部分。 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。一个线程,当阻塞的时候,不占用CPU资源。
static object locker = new object();
static void Add()
{
for (int i = ; i < ; i++)
{
lock (locker)
idx++;
}
}
- Interlocked类用于使变量的简单语句原子话(最小执行单元,不会被中途打断),提供了以线程安全的方式递增、递减、交换和读取值的方法。
对上例而言,把idx++替换成Interlocked.Increment(ref idx);
- Monitor类算是实现锁机制的纯正类,lock语句由编译器解析为使用Monitor类。
lock(obj)
{
//synchronized region for obj
} 相当于 Monitor.Enter(obj);
try
{
//synchornized region for obj
}
finally
{
Monitor.Exit(obj)
}
用TryEnter可以添加timeout
object obj = new object();
Task.Run(()=>{
lock(obj)
{
Console.WriteLine("lock obj");
Thread.Sleep();
}
});
bool b = Monitor.TryEnter(obj, );
if (b)
{
try
{
Console.WriteLine("monitor enter.");
}
finally
{
Monitor.Exit(obj);
}
}
else
{
Console.WriteLine("monitor enter false.");
} Console.ReadKey();
另外,Monitor还提供了Wait方法,用于释放对象上的锁并阻止当前线程,直到它重新获取该锁。
提供了Pulse方法用于通知等待队列中的线程锁定对象状态的更改;PulseAll通知所有的等待线程对象状态的更改。
- SpinLock自旋锁,如果基于对象锁定(Monitor)的系统开销由于垃圾回收而过高,就可以使用SpinLock结构。如果有大量的锁定(例如,列表中的每个节点都有一个锁定),且锁定的时间总是非常短,SpinLock结构就很有用。应避免使用多个SpinLock结构,也不要调用任何可能阻塞的内容。SpinLock 应仅用于您,这样做可以改进应用程序的性能确定后。 还有一点需要注意 SpinLock 是值类型,为了提高性能。 出于此原因,您必须非常小心,以免意外复制 SpinLock 实例,因为两个实例 (原始项和副本) 都将完全相互独立的这可能会导致错误行为的应用程序。 如果 SpinLock 必须围绕传递实例,则应通过引用而不是通过值传递。
请不要在存储 SpinLock 只读字段中的实例。
最新文章
- JavaScript之单例实战
- 《DSP using MATLAB》示例Example5.20
- SQL Server Reporting Service(SSRS) 第二篇 SSRS数据分组Parent Group
- spring访问静态资源出错,No mapping found for HTTP request with URI xxx/resources/js/jquery.min.js...
- Loadrunner11.0 录制手机App脚本的方法
- Setup Apache + PHP + MySql on Windows 10
- hdu4291之矩阵快速幂
- 深入浅出ExtJS 第二章 Ext框架基础
- android之模拟器更新底层
- 在CentOS6.0上安装Oracle 11gR2 (11.2.0.1)以及基本的配置(一)
- jQeury学习笔记
- ThinkPHP - 每个操作都检测用户是否登录
- Quartz(GUI)图形界面程序----Quartz Web
- MySQL GTIDs(global transaction identifiers)
- 微信小程序换皮肤,动态切换菜单栏和导航栏的样式,动态修改TabBar和NavigationBar
- ubuntu中minicom安装和使用
- JS读取.properties文件的方法
- linux --- 1.初始linux
- 抠图|计蒜客2019蓝桥杯省赛 B 组模拟赛(一)
- DB2与Sybase/Oracle/Informix的比较
热门文章
- 二、第一个ECharts图表
- 【Codeforces 264B】Good Sequences
- SGU -1500 - Pass Licenses
- Sql语句中关于如何在like &#39;%?%&#39;中给?赋值
- Just a Hook 线段树 区间更新
- kendo grid 点击新增没有反映
- mysql MVCC原理理解
- com.101tec.ZKClient实现中的subscribeDataChanges设置的监听器事件不回调的问题研究
- java中POJO类和DTO类都要实现序列化
- Workflow:添加工作流存储功能