C#多线程编程的同步也线程安全
前一篇文章记录了简单的多线程编程的几种方式,但是在实际的项目中,也需要等待多线程执行完成之后再执行的方法,这个就叫做多线程的同步,或者,由于多个线程对同一对象的同时操作造成数据错乱,需要线程安全。这篇文章主要记录多线程的同步异步如何实现线程安全的几种方式的笔记,如有错误,请大神不吝赐教。
因为代码里面有很详细的注释,所以下面直接附上代码,不做过多的解释,如有疑问可以百度相关主题的文章详细了解。
1、 Mutex
////1.Mutex测试
////Mutex互斥锁,用于多线程间的线程同步通过WaitOne等待当前锁定的线程执行完成,例如,线程B执行需要等待线程A执行结束的情况下,可以使用Mutex
////同时Mutex还有一个比较有趣的功能就是可以设置实现客户端在同一太电脑上只能打开一个进程
//bool createNew = false;
//Mutex mutex = new Mutex(true, "MutexTest", out createNew);
//AutoResetEvent ae = new AutoResetEvent(false);//定义一个信号量,表示执行结束,可以释放互斥锁
////参数1表示初始化的时候当前互斥锁是否已被获取,false代表未被获取,
////参数2标识当前互斥锁的名称,指定一个名称,配合参数3即可实现只能开启一个进程的效果
////参数3表示是否创建了一个新的互斥锁
//Thread t1 = new Thread(new ThreadStart(() =>
//{
// Console.WriteLine("我是线程1");
// Console.WriteLine("线程1开始执行!");
// Thread.Sleep(1000);//线程休眠1秒钟,用于模拟需要较长时间执行的功能
// Console.WriteLine("线程1执行结束!");
// ae.Set();
//}));
//Thread t2 = new Thread(() =>
// {
// Console.WriteLine("我是线程2");
// Console.WriteLine("线程2开始执行!");
// mutex.WaitOne();//等待互斥锁被释放,模拟实际项目中需要其他线程执行完毕方可执行的功能
// Console.WriteLine("线程2执行结束!");
// });
////因为是多线程执行,所以线程1与线程2的谁先开始执行,以上代码中未进行控制,
////但线程2一定是在线程1执行完成之后才能结束
//t1.Start();
//t2.Start();
//ae.WaitOne();//等待释放信息
//mutex.ReleaseMutex();//释放互斥锁
////AutoResetEvent的功能类似于一个红绿灯信号,当达到可以释放的条件的时候,调用Set方法来通知后续代码可以执行了,
////此处为何需要一个信号,是因为Mutex定义在主线程中,如果在异步线程中释放,会报一个错,提示在不安全的代码块中执行
////互斥锁,所以此处使用信号来通知主线程可以释放互斥锁了
2、AutoResetEvent
/// <summary>
/// 通过AutoRestEvent实现线程同步
/// </summary>
public void TestAutoResetEvent()
{
AutoResetEvent[] autoResetEvents = new AutoResetEvent[3];
autoResetEvents[0] = new AutoResetEvent(false);//定义初始信号为关
autoResetEvents[1] = new AutoResetEvent(false);
autoResetEvents[2] = new AutoResetEvent(false);
//以下代码实现线程1结束之后线程2才能结束,线程2结束之后线程3才能开始,所有线程都结束之后主线程才能继续
Thread t1 = new Thread(new ThreadStart(() =>
{
Console.WriteLine("线程1开始!");
Thread.Sleep(1000);
Console.WriteLine("线程1结束!");
autoResetEvents[0].Set();
}));
Thread t2 = new Thread(new ThreadStart(() =>
{
Console.WriteLine("线程2开始!");
Thread.Sleep(1000);
autoResetEvents[0].WaitOne();
Console.WriteLine("线程2结束!");
autoResetEvents[1].Set();
}));
Thread t3 = new Thread(new ThreadStart(() =>
{
autoResetEvents[1].WaitOne();
Console.WriteLine("线程3开始!");
Thread.Sleep(1000);
Console.WriteLine("线程3结束!");
autoResetEvents[2].Set();
}));
t1.Start();
t2.Start();
t3.Start();
Console.WriteLine("主线程开始等待......");
autoResetEvents[2].WaitOne();//等待所有线程结束
//AutoResetEvent从字面即可知道是自动信号,意思为当信号被捕捉之后会自动重置为关闭状态
//对应的ManualResetEvent为手动信号,使用方法相同但是在被捕捉之后不会被重置为关闭状态
//需要手动调用Reset方法关闭信号,如果是简单的同步,使用自动信号即可,如果需要很复杂的流程控制
//可以使用自动信号,同时可以配合WaitHandle来实现线程的同步,WaitHandle拥有WaitAny方法等待任意一个信号
//WaitAll方法等待所有信号,使用方法与信号的WaiOne相似,此处不再进行举例,可以查看相关文章具体了解
Console.WriteLine("主线程执行结束!");
}
3、 lock与Monitor
/// <summary>
/// 测试lock和Monitor实现线程安全的多线程
/// </summary>
public void TestLockAndMonitor()
{
//lock与monitor实现相同的功能,多线程的线程安全
//lock实际上就是Monitor.Enter与Monitor.Exit的语法糖
object obj = new object();//创建一个应用类型用于lock
int count = 1;
int sum = 0;
for (int i = 0; i < 20; i++)
{
Thread t = new Thread(new ThreadStart(() =>
{
for (int j = 0; j < 1000; j++)
{
//此处保证线程安全的原理是,当前多个线程同时访问count的时候,如果不lock
//可能多个线程访问到的count是相同的值,这样虽然多个线程都执行了count++但是
//结果却没有加上去,造成最终的结果错误,当lock之后,lock内部的代码每次只能
//有一个线程访问,所以每个线程获取的count都不可能相同,这样就能保证最后的结果一定是正确的
//lock (obj)//取消此句代码测试多线程的不安全性,取消之后可能每次执行的结果都不一样
//{
// sum += count;
// count++;
//}
//使用下面的方法与使用lock的功能相同
//Monitor.Enter(obj);
//sum += count;
//count++;
//Monitor.Exit(obj);
}
}));
t.Start();
}
Thread.Sleep(3000);//延时3秒保证异步线程全部执行完成
Console.WriteLine(sum);
}
4、信号量
/// <summary>
/// 测试信号量实现线程安全
/// </summary>
public void TestSemaphore()
{
//Semaphore 类似于线程池,用于设置同时可以有多少个线程执行
//当线程超过信号量运行的最大值之后,后续的线程就需要等待
Semaphore semaphore = new Semaphore(2, 2);//用于设置最大可以有两个线程同时执行,初始时有两个位置空闲
for (int i = 0; i <= 20; i++)
{
Thread t = new Thread(new ThreadStart(() =>
{
semaphore.WaitOne();//等待信号释放,若未超过信号的最大数值,则不需等待
Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
Random random = new Random();
Thread.Sleep(random.Next(1,5) * 1000);//随机休眠1-5秒
semaphore.Release();//释放当前信号
}));
t.Start();
}
}
5、 自旋锁
/// <summary>
/// 测试自旋锁
/// </summary>
public void TestSpinLocked()
{
//自旋锁与lock实现的功能相同,但是lock锁住对象开销比较大
//相反自旋锁开销比较小,效率相对也比lock高,当锁住的次数比较多,同时锁的时间比较短的时候,可是使用自旋锁
int count = 1;
int sum = 0;
SpinLock spinLock = new SpinLock();
for (int i = 0; i < 20; i++)
{
Thread t = new Thread(new ThreadStart(() =>
{
bool lockTaken = false;
//使用下面的方法与使用lock的功能相同
//申请获取锁
spinLock.Enter(ref lockTaken);
for (int j = 0; j < 1000; j++)
{
sum += count;
count++;
}
if (lockTaken) //判断当前线程是否锁住,如果锁住则释放它,防止出现死锁的情况
{
spinLock.Exit();
}
}));
t.Start();
}
Thread.Sleep(3000);//延时3秒保证异步线程全部执行完成
Console.WriteLine(sum);
}
6、 原子操作
/// <summary>
/// 测试InterLocked
/// </summary>
public void TestInterLocked()
{
//InterLocked拥有几个方法来保证线程安全,每个操作都是原子级的,所以效率高,线程安全
//此方法使用InterLocked实现类似于自旋锁的功能
//关于InterLocked的更多用法请参考MSDN
double current = 0;
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(new ThreadStart(() =>
{
while (Interlocked.Exchange(ref current, 1) == 1)
{
//此循环用于等待当前捕获current的线程执行结束
}
Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
Random random = new Random();
Thread.Sleep(random.Next(1, 3) * 1000);//随机休眠1-3秒
Interlocked.Exchange(ref current, 0);//将current重置为0
}));
t.Start();
}
}
以上的代码仅仅是笔记用途,没有深入讲解各个方式的优缺点及用途,只是大概的解释知道的这些方法,有兴趣的话,大家可以结合每一个主题的文章详细了解其用法及优缺点,谢谢!
C#多线程编程的同步也线程安全的更多相关文章
- 数据结构(逻辑结构,物理结构,特点) C#多线程编程的同步也线程安全 C#多线程编程笔记 String 与 StringBuilder (StringBuffer) 数据结构与算法-初体验(极客专栏)
数据结构(逻辑结构,物理结构,特点) 一.数据的逻辑结构:指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关.逻辑结构包括: 集合 数 ...
- Linux多线程编程,为什么要使用线程,使用线程的理由和优点等
线程?为什么有了进程还需要线程呢,他们有什么区别?使用线程有什么优势呢?还有多线程编程的一些细节问题,(http://www.0830120.com)如线程之间怎样同步.互斥,这些东西将在本文中介绍. ...
- 【C/C++多线程编程之五】pthread线程深入理解
多线程编程之pthread线程深入理解 Pthread是 POSIX threads 的简称,是POSIX的线程标准. 前几篇博客已经能给你初步的多线程概念.在进一步学 ...
- python 多线程编程之进程和线程基础概念
多线程编程 在多线程(multithreaded,MT)出现之前,计算机程序的执行都是由单个步骤序列组成的,该序列组合在主机的CPU中按照同步顺序执行.无论是任务本身需要按照步骤顺序执行,还是整个过程 ...
- C++多线程编程(三)线程间通信
多线程编程之三——线程间通讯 作者:韩耀旭 原文地址:http://www.vckbase.com/document/viewdoc/?id=1707 七.线程间通讯 一般而言,应用程序中的一个次要线 ...
- C#多线程编程(1)--线程,线程池和Task
新开了一个多线程编程系列,该系列主要讲解C#中的多线程编程. 利用多线程的目的有2个: 一是防止UI线程被耗时的程序占用,导致界面卡顿:二是能够利用多核CPU的资源,提高运行效率. 我没有进行很 ...
- Qt多线程编程中的对象线程与函数执行线程
近来用Qt编写一段多线程的TcpSocket通信程序,被其中Qt中报的几个warning搞晕了,一会儿是说“Cannot create children for a parent that is in ...
- C#多线程编程(5)--线程安全1
当你需要2个线程读写同一个数据时,就需要数据同步.线程同步的办法有:(1)原子操作:(2)锁.原子操作能够保证该操作在CPU内核中不会被"拆分",锁能够保证只有一个线程访问该数据, ...
- 使用Java 多线程编程 让三个线程轮流输出ABC,循环10次后结束
简要分析: 要求三个线程轮流输出,这里我们要使用一个对象锁,让关键部分的代码放入同步块当中.同时要有一个变量记录打印的次数到达10次循环后不再打印,另外一个就是要给每个线程一个标志号,我们根据标识号来 ...
随机推荐
- Windows下安装Kafka
一.安装JDK 二.安装zooeleeper 下载安装包:http://zookeeper.apache.org/releases.html#download 下载后解压到一个目录: 1.进入Zook ...
- LeetCode 26 Remove Duplicates from Sorted Array [Array/std::distance/std::unique] <c++>
LeetCode 26 Remove Duplicates from Sorted Array [Array/std::distance/std::unique] <c++> 给出排序好的 ...
- 浅谈C++ STL
C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量.链表.队列.栈. C++ 标准模板库的核心包括以 ...
- Servlet 过滤器Filter
特点 1)Filter是依赖于Servlet容器,属于Servlet规范的一部分,在Servlet API中定义了三个接口类:Filter, FilterChain, FilterConfig. 2) ...
- Mybatis Mapper文件中的一小坑
前几天来一需求,实现过程中需要修改一个底层的查询接口,具体修改就是在where中添加一个条件,由于这个底层SQL使用的地方太多,所以就想着是用if加一标识符做个判断,传一个只有我会使用的参数,然后动态 ...
- ivew ui
render操作: render:(h, params) => { return h('div', [ h('Button',{ on:{ click:()=>{ this.edit(pa ...
- css基础样式
1.行间样式:在标签中添加<style>属性 格式:标签名 style="样式:样式值1;样式2=样式值2" 2.内嵌样式:在<head>&l ...
- 折线图hellocharts的使用说明
以前用过一次XCL-chart,但是感觉只适合固定图表,不去滑动的那种,因为你一滑动太卡了你懂得(毕竟作者好久没更新优化了),拙言大神我开玩笑的 ,毕竟我加你的群大半年了 - - 第二研究了一下ach ...
- svn错误:更新源码出现校验和不匹配问题
svn 的文本校验和不匹配: 最近在更新自动化源代码的时候出现了一个错误:svn: Checksum mismatch while updating.... 查了下google,原来是在更新源码出现校 ...
- linux 下vim中关于删除某段,某行,或者全部删除的命令 ZZ
1,先打开某个文件: vim filename 2,转到文件结尾 在命令模式输入 G 3,转到10行 在命令模式输入 10G 4,删除所有内容:先用G 转到文件尾,然后使用下面命令: :1, .d 5 ...