C# 线程同步的多种方式
实际应用中多个线程往往需要共享数据,因此必须使用同步技术,确保一次只有一个线程访问和改变共享数据。同步又分为进程内部线程的同步以及进程之间线程的同步。
进程内部线程同步:
1. lock : 使用比较简单 lock(obj){ Synchronize part }; 只能传递对象,无法设置等待超时;
2. InterLocked: 原子操作,提供了以线程安全的方式递增,递减,交换和读取值的方法;
3. Monitor: lock语句等同于Monitor.Enter() ,同样只能传递对象,无法设置等待超时,如下:
Monitor.Enter(obj){
//Synchronized part
}finally{
Monitor.Exit(obj);
}
另外使用Monitor.TryEnter(),可以传递等待超时,若获取锁,则布尔参考变量设为true,执行同步操作;若超时未获取锁,则布尔参考变量设为false,执行其他操作; 如下:
bool lockTaken=false;
Monitor.TryEnter(obj, 500, ref lockTaken);
if(lockTaken){
try
{
//Synchronized part
}
finally
{
Monitor.Exit(obj);
}
}else{
//don't aquire the lock, excute other parts
}
进程之间线程同步:
1. WaitHandle: 一个抽象基类,用于等待一个信号的设置。 常用方法如下:
WaitOne(): 等待一个信号的出现,可设置超时;
WaitAll(): 等待多个信号的出现,可设置超时;
WaitAny(): 等待任意一个信号的出现,可设置超时;
Mutex类(Mutual Exclusion 互斥),EventWaitHandle类,Semaphore类 均派生自WaitHandle类。
2. Mutex: 与Monitor 类似,只有一个线程能够获取锁定。利用WaitOne() 获取锁定,利用ReleaseMutex() 解除锁定。构造函数使用如下:
bool isNew = false;
mutex = new Mutex(false, "Mutex1", out isNew);
参数1:锁创建后是否由主调线程拥有。 如果设为true,相当于调用了WaitOne(),需要释放,否则其他线程无法获取锁;
参数2:锁名称,可通过OpenExist()或TryOpenExist() 打开已有锁,因为操作系统识别有名称的互锁,所以可由不同的进程共享。若锁名称为空,就是未命名的互锁,不能在多个进程之间共享;
参数3: 是否为新创建的互锁;
下面的例子演示Mutex 在进程之间的使用:
class Program
{
private static Mutex mutex = null;
static void Main(string[] args)
{
bool isNew = false;
mutex = new Mutex(false, "Mutex1", out isNew);
Console.WriteLine("Main Start....");
mutex.WaitOne();
Console.WriteLine("Aquire Lock and Running....");
Thread.Sleep(10000);
mutex.ReleaseMutex();
Console.WriteLine("Release Lock....");
Console.WriteLine("Main end....");
Console.ReadLine();
}
}
连续2次运行这个控制台程序的exe,结果如下,首先运行的获取 Mutex1 互锁, 后面运行的会等待直到前面运行的释放 Mutex1 互锁。
3.Semaphore: 信号量的作用于互斥锁类似,但它可以定义一定数量的线程同时使用。下面是构造函数:
bool isNew = false;
semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
参数1:创建后,最初释放的锁的数量,如参数1设为2,参数2设为3,则创建后只有2个锁可用,另1个已经锁定;
参数2:定义可用锁的数量;
参数3: 信号量的名称,与Mutex类似;
参数4:是否为新创建的互锁;
以下例子创建了信号量“semaphore1”,利用Parallel.For() 同步运行Func1() ,在Func1() 中,当线程获取信号量锁,释放锁或等待超时,都会在控制台里输出,
class Program
{
private static Semaphore semaphore = null;
static void Main(string[] args)
{ Console.WriteLine("Main Start....");
bool isNew = false;
semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
Parallel.For(0, 6, Func1);
Console.WriteLine("Main end....");
Console.ReadLine();
} static void Func1(int index)
{
Console.WriteLine("Task {0} Start....",Task.CurrentId);
bool isComplete = false;
while (!isComplete)
{
if (semaphore.WaitOne(1000))
{
try
{
Console.WriteLine("Task {0} aquire lock....", Task.CurrentId);
Thread.Sleep(5000);
}
finally
{
semaphore.Release();
Console.WriteLine("Task {0} release lock....", Task.CurrentId);
isComplete = true;
}
}
else
{
Console.WriteLine("Task {0} timeout....", Task.CurrentId);
}
}
}
运行结果如下,线程1,2,3首先获取信号量锁,线程4,5,6在等待,直到1,2,3释放,
Main Start....
Task 1 Start....
Task 1 aquire lock....
Task 2 Start....
Task 2 aquire lock....
Task 3 Start....
Task 3 aquire lock....
Task 4 Start....
Task 5 Start....
Task 6 Start....
Task 4 timeout....
Task 5 timeout....
Task 6 timeout....
Task 5 timeout....
Task 4 timeout....
Task 6 timeout....
Task 4 timeout....
Task 5 timeout....
Task 6 timeout....
Task 4 timeout....
Task 5 timeout....
Task 6 timeout....
Task 5 aquire lock....
Task 1 release lock....
Task 4 aquire lock....
Task 6 aquire lock....
Task 2 release lock....
Task 3 release lock....
Task 5 release lock....
Task 4 release lock....
Task 6 release lock....
Main end....
4. AutoResetEvent 类:可以使用事件通知其他任务,构造函数为 public AutoResetEvent(bool initialState)。
当initialState=true,处于signaled 模式(终止状态),调用waitone() 也不会阻塞任务,等待信号,调用Reset()方法,可以设置为non-signaled 模式;
当initialState=fasle,处于non-signaled 模式(非终止状态),调用waitone() 会等待信号阻塞当前线程(可以在多个线程中调用,同时阻塞多个线程),直到调用set()发送信号释放线程(调用一次,只能释放一个线程),一般使用这种方式;
以下例子创建5个任务,分别调用waitone()阻塞线程,接着每隔2s 调用set(),
private static AutoResetEvent autoReset = new AutoResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("Main Start....");
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Console.WriteLine("{0} Start....", Task.CurrentId);
autoReset.WaitOne();
Console.WriteLine("{0} Continue....", Task.CurrentId);
});
}
for (int i = 0; i < 5;i++ )
{
Thread.Sleep(2000);
autoReset.Set();
}
Console.WriteLine("Main end....");
Console.ReadLine();
}
运行结果每次顺序略有不同,释放是随机的:
Main Start....
1 Start....
2 Start....
3 Start....
4 Start....
5 Start....
3 Continue....
1 Continue....
4 Continue....
2 Continue....
Main end....
5 Continue....
5. ManualResetEvent 类:功能基本上和AutoSetEvent类似,但又一个不同点:
使用AutoSetEvent,每次调用set(),切换到终止模式,只能释放一个waitone(),便会自动切换到非终止模式;但ManualResetEvent,调用set(),切换到终止模式,可以释放当前所有的waitone(),需要手动调用reset()才能切换到非终止模式。
以下例子说明了这个不同的:
private static ManualResetEvent manualReset = new ManualResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("Main Start....");
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Console.WriteLine("{0} Start....", Task.CurrentId);
manualReset.WaitOne();
Console.WriteLine("{0} Continue....", Task.CurrentId);
});
}
Thread.Sleep(2000);
manualReset.Set();
manualReset.WaitOne();
Console.WriteLine("it doesn't work now, Main continue....");
manualReset.Reset();
manualReset.WaitOne();
Console.WriteLine("Main end....");
Console.ReadLine();
}
运行结果:
Main Start....
1 Start....
2 Start....
3 Start....
4 Start....
5 Start....
5 Continue....
4 Continue....
3 Continue....
2 Continue....
it doesn't work now, Main continue....
1 Continue....
C# 线程同步的多种方式的更多相关文章
- 线程同步的实现方式(volatile、synchronized、CountDownLatch)
题目: 自定义容器,提供新增元素(add)和获取元素数量(size)方法.启动两个线程. 线程1向容器中新增10个数据.线程2监听容器元素数量,当容器元素数量为5时,线程2输出信息并终止. 方法一:v ...
- java笔记--关于线程同步(7种同步方式)
关于线程同步(7种方式) --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3897440.html"谢谢-- 为何要使用同步? ...
- C# 线程同步的三类情景
C# 已经提供了我们几种非常好用的类库如 BackgroundWorker.Thread.Task等,借助它们,我们就能够分分钟编写出一个多线程的应用程序. 比如这样一个需求:有一个 Winform ...
- C#线程同步技术(二) Interlocked 类
接昨天谈及的线程同步问题,今天介绍一个比较简单的类,Interlocked.它提供了以线程安全的方式递增.递减.交换和读取值的方法. 它的特点是: 1.相对于其他线程同步技术,速度会快很多. 2.只能 ...
- java笔记--关于线程同步(5种同步方式)【转】
为何要使用同步? java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完 ...
- Windows API学习---用户方式中的线程同步
前言 当所有的线程在互相之间不需要进行通信的情况下就能够顺利地运行时, Micrsoft Windows的运行性能最好.但是,线程很少能够在所有的时间都独立地进行操作.通常情况下,要生成一些线程来处理 ...
- Java线程同步的方式
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的 ...
- IOS 多线程,线程同步的三种方式
本文主要是讲述 IOS 多线程,线程同步的三种方式,更多IOS技术知识,请登陆疯狂软件教育官网. 一般情况下我们使用线程,在多个线程共同访问同一块资源.为保护线程资源的安全和线程访问的正确性. 在IO ...
- C++线程同步的四种方式(Windows)
为什么要进行线程同步? 在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作.更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解.正常情况下对这种处理结果的 ...
随机推荐
- python 按行查找文本文件,找出答案,并提示置顶答案
1.整理好答案文件为文本文件:不能有空行:每个题干前有数字做为题号:每个题答案第一个字符为字母,答案占一行import time import time import sys import os im ...
- Python+Request库+第三方平台实现验证码识别示例
1.登录时经常的出现验证码,此次结合Python+Request+第三方验证码识别平台(超级鹰识别平台) 2.首先到超级鹰平台下载对应语言的识别码封装,超级鹰平台:http://www.chaojiy ...
- CSP-S 2020 初赛
游记 游记个鬼啊跨条街就到了=-= 不分Day反正就一天. 9:30开考,8:30起床. 下雨了,一出宿舍门整个学校全被白色的雾气笼罩.愚以为这是祥瑞之气,昨夜似有麒麟貔貅路过,祝我今日初赛RP++ ...
- 第四篇--git 上传可能出现的问题
1. Q:fatal: TaskCanceledException encountered. A task was canceled. A:$ git config --system --unset ...
- NetCoreMvc系列文章02---依赖注入
.netCore自带依赖注入,支持构造函数注入,如不了解IOC 和DI 思想的请看我其它文章中关于这主面的介绍.如Startup.cs类中的Configure方法其中IApplicationBuild ...
- Scrapy+splash报错 Connection was refused by other side
报错信息如下: Traceback (most recent call last): File "/usr/local/lib/python3.7/site-packages/scrap ...
- 深入刨析tomcat 之---第6篇 how tomcat works 第5章 容器实现原理
writedby 张艳涛
- (论文笔记Arxiv2021)Walk in the Cloud: Learning Curves for Point Clouds Shape Analysis
目录 摘要 1.引言 2.相关工作 3.方法 3.1局部特征聚合的再思考 3.2 曲线分组 3.3 曲线聚合和CurveNet 4.实验 4.1 应用细节 4.2 基准 4.3 消融研究 5.总结 W ...
- 🏆(不要错过!)【CI/CD技术专题】「Jenkins实战系列」(3)Jenkinsfile+DockerFile实现自动部署
每日一句 没有人会因学问而成为智者.学问或许能由勤奋得来,而机智与智慧却有懒于天赋. 前提概要 Jenkins下用DockerFile自动部署Java项目,项目的部署放心推向容器化时代机制. 本节需要 ...
- 组合数取模(lukas)板子
求 C(n,m)%mo #include<bits/stdc++.h>#define re register int#define mo 10007#define ll long long ...