前一篇文章记录了简单的多线程编程的几种方式,但是在实际的项目中,也需要等待多线程执行完成之后再执行的方法,这个就叫做多线程的同步,或者,由于多个线程对同一对象的同时操作造成数据错乱,需要线程安全。这篇文章主要记录多线程的同步异步如何实现线程安全的几种方式的笔记,如有错误,请大神不吝赐教。

因为代码里面有很详细的注释,所以下面直接附上代码,不做过多的解释,如有疑问可以百度相关主题的文章详细了解。

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#多线程编程的同步也线程安全的更多相关文章

  1. 数据结构(逻辑结构,物理结构,特点) C#多线程编程的同步也线程安全 C#多线程编程笔记 String 与 StringBuilder (StringBuffer) 数据结构与算法-初体验(极客专栏)

    数据结构(逻辑结构,物理结构,特点) 一.数据的逻辑结构:指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关.逻辑结构包括: 集合 数 ...

  2. Linux多线程编程,为什么要使用线程,使用线程的理由和优点等

    线程?为什么有了进程还需要线程呢,他们有什么区别?使用线程有什么优势呢?还有多线程编程的一些细节问题,(http://www.0830120.com)如线程之间怎样同步.互斥,这些东西将在本文中介绍. ...

  3. 【C/C++多线程编程之五】pthread线程深入理解

    多线程编程之pthread线程深入理解       Pthread是 POSIX threads 的简称,是POSIX的线程标准.           前几篇博客已经能给你初步的多线程概念.在进一步学 ...

  4. python 多线程编程之进程和线程基础概念

    多线程编程 在多线程(multithreaded,MT)出现之前,计算机程序的执行都是由单个步骤序列组成的,该序列组合在主机的CPU中按照同步顺序执行.无论是任务本身需要按照步骤顺序执行,还是整个过程 ...

  5. C++多线程编程(三)线程间通信

    多线程编程之三——线程间通讯 作者:韩耀旭 原文地址:http://www.vckbase.com/document/viewdoc/?id=1707 七.线程间通讯 一般而言,应用程序中的一个次要线 ...

  6. C#多线程编程(1)--线程,线程池和Task

    新开了一个多线程编程系列,该系列主要讲解C#中的多线程编程.    利用多线程的目的有2个: 一是防止UI线程被耗时的程序占用,导致界面卡顿:二是能够利用多核CPU的资源,提高运行效率. 我没有进行很 ...

  7. Qt多线程编程中的对象线程与函数执行线程

    近来用Qt编写一段多线程的TcpSocket通信程序,被其中Qt中报的几个warning搞晕了,一会儿是说“Cannot create children for a parent that is in ...

  8. C#多线程编程(5)--线程安全1

    当你需要2个线程读写同一个数据时,就需要数据同步.线程同步的办法有:(1)原子操作:(2)锁.原子操作能够保证该操作在CPU内核中不会被"拆分",锁能够保证只有一个线程访问该数据, ...

  9. 使用Java 多线程编程 让三个线程轮流输出ABC,循环10次后结束

    简要分析: 要求三个线程轮流输出,这里我们要使用一个对象锁,让关键部分的代码放入同步块当中.同时要有一个变量记录打印的次数到达10次循环后不再打印,另外一个就是要给每个线程一个标志号,我们根据标识号来 ...

随机推荐

  1. 面试题:int和Integer的区别

    java底层源码:  -128  127之间

  2. C++ STL编程轻松入门【转载】

    1 初识STL:解答一些疑问 1.1 一个最关心的问题:什么是STL "什么是STL?",假如你对STL还知之甚少,那么我想,你一定很想知道这个问题的答案,坦率地讲,要指望用短短数 ...

  3. 【高并发架构】Redis特点及构件模型

    数据结构 redis 相比 memcached 来说,拥有更多的数据结构,能支持更丰富的数据操作.如果需要缓存能够支持更复杂的结构和操作, redis 会是不错的选择. redis 主要有以下几种数据 ...

  4. db2数据库常见问题

    db2数据库不能轻易改变表结构,不然表会进入暂挂状态,造成表被锁住. 解锁表语句:call sysproc.admin_cmd('reorg table <table name>');

  5. 初学Java的那段日子

    最近因为一个朋友想要学习Java,在帮助他找教程的过程中回想到了我自己当年学习Java的那段岁月,故写了此篇文章总结了一下初学Java所必须要掌握的知识点,然后把一部分常见的面试题罗列出来.给予刚刚开 ...

  6. [Swift]LeetCode161. 一次编辑距离 $ One Edit Distance

    Given two strings S and T, determine if they are both one edit distance apart. 给定两个字符串S和T,确定它们是否都是是一 ...

  7. [Swift]LeetCode172. 阶乘后的零 | Factorial Trailing Zeroes

    Given an integer n, return the number of trailing zeroes in n!. Example 1: Input: 3 Output: 0 Explan ...

  8. CentOS随笔——Service与防火墙关闭

    Service后台服务管理 基本语法 service 服务名 start 开启服务 service 服务名 stop 关闭服务 service 服务名 restart 重启服务 service 服务名 ...

  9. 一文掌握 Linux 性能分析之网络篇(续)

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 这是 Linu ...

  10. Map 转换成byte[] 数组

    把Map转换成byte数组,使用 ByteArrayOutputStream和ObjectOutputStream Map<String,String> map = new HashMap ...