C# 多线程编程第二步——线程同步与线程安全
上一篇博客学习了如何简单的使用多线程。其实普通的多线程确实很简单,但是一个安全的高效的多线程却不那么简单。所以很多时候不正确的使用多线程反倒会影响程序的性能。
下面先看一个例子 :
class Program
{
static int num = ; static void Main(string[] args)
{
Stopwatch stopWatch = new Stopwatch(); //开始计时
stopWatch.Start(); ThreadStart threadStart = new ThreadStart(Run); for (int i = ; i < ; i++)
{
Thread thread = new Thread(threadStart);
thread.Start();
} num++;
Console.WriteLine("num is:" + num);
Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //停止计时
stopWatch.Stop(); //输出执行的时间,毫秒数
Console.WriteLine("The execution time is " + stopWatch.ElapsedMilliseconds + " milliseconds.");
Console.ReadKey();
} public static void Run()
{
num++;
Console.WriteLine("num is:" + num);
Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
}
}
执行结果:

从上面可以看出变量 num 的值不是连续递增的,输出也是没有顺序的,而且每次输出的值都是不一样的,这是因为异步线程同时访问一个成员时造成的,所以这样的多线程对于我们来说是不可控的。以上这个例子就是非线程安全的,那么要做到线程安全就需要用到线程同步。线程同步有很多种方法,比如之前用到过的 Join() 方法,它也可以实现线程的同步。下面我们来试试:
class Program
{
static int num = ; static void Main(string[] args)
{
Stopwatch stopWatch = new Stopwatch(); //开始计时
stopWatch.Start(); ThreadStart threadStart = new ThreadStart(Run); for (int i = ; i < ; i++)
{
Thread thread = new Thread(threadStart);
thread.Start();
thread.Join();
} num++;
Console.WriteLine("num is:" + num);
Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //停止计时
stopWatch.Stop(); //输出执行的时间,毫秒数
Console.WriteLine("The execution time is " + stopWatch.ElapsedMilliseconds + " milliseconds.");
Console.ReadKey();
} public static void Run()
{
num++;
Console.WriteLine("num is:" + num);
Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
}
}
执行结果:

这样就实现了简单的同步,相比起上面的代码也就只是添加了一行代码(thread.Join();),之前也提到了 Join() 这个方法用于阻止当前线程,直到前面的线程执行完成。可是这样虽然是实现了同步,但是却也阻塞了主线程的继续执行,这样和单线程貌似没什么区别了。既然这样我们再去学习一下其他的方法。
实现线程同步还有一种锁的机制,下面是一种最简单的锁机制,即使用 lock。如下:
class Program
{
private object locker = new object();
int num = ; static void Main(string[] args)
{
Program program = new Program();
Stopwatch stopWatch = new Stopwatch(); //开始计时
stopWatch.Start(); ThreadStart threadStart = new ThreadStart(program.Run); for (int i = ; i < ; i++)
{
Thread thread = new Thread(threadStart);
thread.Start();
} program.num++;
Console.WriteLine("num is:" + program.num);
Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //停止计时
stopWatch.Stop(); //输出执行的时间,毫秒数
Console.WriteLine("The execution time is " + stopWatch.ElapsedMilliseconds + " milliseconds.");
Console.ReadKey();
} public void Run()
{
lock (locker)
{
num++;
Console.WriteLine("num is:" + num);
Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
}
}
}
执行结果:

lock 是一种比较好用的简单的线程同步方式,它是通过为给定对象获取互斥锁来实现同步的。可以看到这种方式的确没有阻塞主线程,而且成员变量的值也是连续递增的,说明是线程安全的。lock 锁机制表示在同一时刻只有一个线程可以锁定同步对象(在这里是locker),任何竞争的的其它线程都将被阻止,直到这个锁被释放。
lock 的参数必须是基于引用类型的对象,不要是基本类型,比如 bool、int,这样根本不能同步,原因是lock的参数要求是对象,如果传入 int,势必要发生装箱操作,这样每次lock的都将是一个新的不同的对象。最好避免使用public类型或不受程序控制的对象实例,因为这样很可能导致死锁。永远也不要 lock 一个字符串。
暂时先到这里,后面学了其他方法在继续更新。
C# 多线程编程第二步——线程同步与线程安全的更多相关文章
- Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介
Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...
- 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)
Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...
- C#多线程编程系列(二)- 线程基础
目录 C#多线程编程系列(二)- 线程基础 1.1 简介 1.2 创建线程 1.3 暂停线程 1.4 线程等待 1.5 终止线程 1.6 检测线程状态 1.7 线程优先级 1.8 前台线程和后台线程 ...
- 从执行上下文角度重新理解.NET(Core)的多线程编程[2]:同步上下文
一般情况下,我们可以将某项操作分发给任意线程来执行,但有的操作确实对于执行的线程是有要求的,最为典型的场景就是:GUI针对UI元素的操作必须在UI主线程中执行.将指定的操作分发给指定线程进行执行的需求 ...
- iOS开发——高级篇——线程同步、线程依赖、线程组
前言 对于iOS开发中的网络请求模块,AFNet的使用应该是最熟悉不过了,但你是否把握了网络请求正确的完成时机?本篇文章涉及线程同步.线程依赖.线程组等专用名词的含义,若对上述名词认识模糊,可先进行查 ...
- C#多线程编程系列(三)- 线程同步
目录 1.1 简介 1.2 执行基本原子操作 1.3 使用Mutex类 1.4 使用SemaphoreSlim类 1.5 使用AutoResetEvent类 1.6 使用ManualResetEven ...
- iOS多线程编程指南(二)线程管理
当应用程序生成一个新的线程的时候,该线程变成应用程序进程空间内的一个实体.每个线程都拥有它自己的执行堆栈,由内核调度独立的运行时间片.一个线程可以和其他线程或其他进程通信,执行I/O操作,甚至执行任何 ...
- Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)
一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...
- Python并发编程-进程 线程 同步锁 线程死锁和递归锁
进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...
随机推荐
- mybatis由浅入深day02_2一对一查询_2.3方法二:resultMap_resultType和resultMap实现一对一查询小结
2.3 方法二:resultMap 使用resultMap,定义专门的resultMap用于映射一对一查询结果. 2.3.1 sql语句 同resultType实现的sql SELECT orders ...
- Oracle-未能加载文件或程序集“oracle.dataaccess”或它的某一个依赖项。试图加载格式不正确的程序。
未能加载文件或程序集“oracle.dataaccess”或它的某一个依赖项.试图加载格式不正确的程序. 说明: 执行当前 Web 请求期间,出现未经处理的异常.请检查堆栈跟踪信息,以了解有关该错误以 ...
- POST提交表单,本地Windows测试无乱码,而将项目部署到服务器端产生乱码原因之一
项目在Windows机上eclipse发布至tomcat测试没有问题,表单提交到数据库的中文都能正常显示,而将项目部署到服务器上时,出现中文乱码 确认POST提交都使用了request.setChar ...
- Linux rdate 命令
rdate命令可以用来查看远程服务器的时间,也可以同步远程服务器的时间到本机 [root@localhost ~]$ yum install -y rdate [root@localhost ~]$ ...
- Python cookielib 模块
什么是 cookie : 指某些网站为了辨别用户身份,进行 session 跟踪而储存在用户本地终端上的数据,通常以 txt 文件形式存储.比如你登录了淘宝,浏览器就会保存 cookie 信息,这样我 ...
- kubectl get 输出格式
常见的输出格式有: * custom-columns=<spec> # 根据自定义列名进行输出,逗号分隔 * custom-columns-file=<filename> # ...
- PyQt4进度条QProgressBar
当我们在处理一个好事较长的任务时,可能就会用到进度条部件.因为使用进度条可以形象告诉用户当前的人物正在进行中.PyQt4工具包提供了水平和垂直两种类型的进度条部件.我们可以设置进度条的最大和最小值,默 ...
- 【BZOJ3156】防御准备 斜率优化
[BZOJ3156]防御准备 Description Input 第一行为一个整数N表示战线的总长度. 第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai. Output 共一个整数,表示最小 ...
- 【BZOJ3387】[Usaco2004 Dec]Fence Obstacle Course栅栏行动 线段树
[BZOJ3387][Usaco2004 Dec]Fence Obstacle Course栅栏行动 Description 约翰建造了N(1≤N≤50000)个栅栏来与牛同乐.第i个栅栏的z坐标为[ ...
- Redis构建处理海量数据的大型购物网站
本系列教程内容提要 Java工程师之Redis实战系列教程教程是一个学习教程,是关于Java工程师的Redis知识的实战系列教程,本系列教程均以解决特定问题为目标,使用Redis快速解决在实际生产中的 ...