在多线程(线程同步)中,我们将学习多线程中操作共享资源的技术,学习到的知识点如下所示:

  • 执行基本的原子操作
  • 使用Mutex构造
  • 使用SemaphoreSlim构造
  • 使用AutoResetEvent构造
  • 使用ManualResetEventSlim构造
  • 使用CountdownEvent构造
  • 使用Barrier构造
  • 使用ReaderWriterLockSlim构造
  • 使用SpinWait构造

 一、执行基本的原子操作

  在这一小节中,我们将学习如何在没有阻塞线程(blocking threads)发生的情况下,在一个对象上执行基本的原子操作并能阻止竞争条件(race condition)的发生。操作步骤如下所示:

1、使用Visual Studio 2015创建一个新的控制台应用程序。

2、双击打开“Program.cs”文件,编写代码如下所示:

  1. using System;
  2. using System.Threading;
  3. using static System.Console;
  4.  
  5. namespace Recipe01
  6. {
  7. abstract class CounterBase
  8. {
  9. public abstract void Increment();
  10.  
  11. public abstract void Decrement();
  12. }
  13.  
  14. class Counter : CounterBase
  15. {
  16. private int count;
  17.  
  18. public int Count => count;
  19.  
  20. public override void Increment()
  21. {
  22. count++;
  23. }
  24.  
  25. public override void Decrement()
  26. {
  27. count--;
  28. }
  29. }
  30.  
  31. class CounterNoLock : CounterBase
  32. {
  33. private int count;
  34.  
  35. public int Count => count;
  36.  
  37. public override void Increment()
  38. {
  39. Interlocked.Increment(ref count);
  40. }
  41.  
  42. public override void Decrement()
  43. {
  44. Interlocked.Decrement(ref count);
  45. }
  46. }
  47.  
  48. class Program
  49. {
  50. static void TestCounter(CounterBase c)
  51. {
  52. for (int i = ; i < ; i++)
  53. {
  54. c.Increment();
  55. c.Decrement();
  56. }
  57. }
  58.  
  59. static void Main(string[] args)
  60. {
  61. WriteLine("Incorrect counter");
  62.  
  63. var c1 = new Counter();
  64.  
  65. var t1 = new Thread(() => TestCounter(c1));
  66. var t2 = new Thread(() => TestCounter(c1));
  67. var t3 = new Thread(() => TestCounter(c1));
  68. t1.Start();
  69. t2.Start();
  70. t3.Start();
  71. t1.Join();
  72. t2.Join();
  73. t3.Join();
  74.  
  75. WriteLine($"Total count: {c1.Count}");
  76. WriteLine("--------------------------");
  77.  
  78. WriteLine("Correct counter");
  79.  
  80. var c2 = new CounterNoLock();
  81.  
  82. t1 = new Thread(() => TestCounter(c2));
  83. t2 = new Thread(() => TestCounter(c2));
  84. t3 = new Thread(() => TestCounter(c2));
  85. t1.Start();
  86. t2.Start();
  87. t3.Start();
  88. t1.Join();
  89. t2.Join();
  90. t3.Join();
  91.  
  92. WriteLine($"Total count: {c2.Count}");
  93. }
  94. }
  95. }

3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

  在第63行代码处,我们创建了一个非线程安全的Counter类的一个对象c1,由于它是非线程安全的,因此会发生竞争条件(race condition)。

  在第65~67行代码处,我们创建了三个线程来运行c1对象的“TestCounter”方法,在该方法中,我们按顺序对c1对象的count变量执行自增和自减操作。由于c1不是线程安全的,因此在这种情况下,我们得到的counter值是不确定的,我们可以得到0值,但多运行几次,多数情况下会得到不是0值得错误结果。

  在多线程(基础篇)中,我们使用lock关键字锁定对象来解决这个问题,但是使用lock关键字会造成其他线程的阻塞。但是,在本示例中我们没有使用lock关键字,而是使用了Interlocked构造,它对于基本的数学操作提供了自增(Increment)、自减(Decrement)以及其他一些方法。

二、使用Mutex构造

  在这一小节中,我们将学习如何使用Mutex构造同步两个单独的程序,即进程间的同步。具体步骤如下所示:

1、使用Visual Studio 2015创建一个新的控制台应用程序。

2、双击打开“Program.cs”文件,编写代码如下所示:

  1. using System;
  2. using System.Threading;
  3. using static System.Console;
  4.  
  5. namespace Recipe02
  6. {
  7. class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. const string MutexName = "Multithreading";
  12.  
  13. using (var m = new Mutex(false, MutexName))
  14. {
  15. // WaitOne方法的作用是阻止当前线程,直到收到其他实例释放的处理信号。
  16. // 第一个参数是等待超时时间,第二个是否退出上下文同步域。
  17. if (!m.WaitOne(TimeSpan.FromSeconds(), false))
  18. {
  19. WriteLine("Second instance is running!");
  20. ReadLine();
  21. }
  22. else
  23. {
  24. WriteLine("Running!");
  25. ReadLine();
  26. // 释放互斥资源
  27. m.ReleaseMutex();
  28. }
  29. }
  30.  
  31. ReadLine();
  32. }
  33. }
  34. }

3、编译代码,执行两次该程序,运行效果如下所示:

第一种情况的运行结果:

第二种情况的运行结果:

  在第11行代码处,我们定义了一个mutex(互斥量)的名称为“Multithreading”,并在第13行代码处将其传递给了Mutex类的构造方法,该构造方法的第一个参数initialOwner我们赋值为false,这允许程序获得一个已经被创建的mutex。如果没有任何线程锁定互斥资源,程序只简单地显示“Running”,然后等待按下任何键以释放互斥资源。

  如果我们启动该程序的第二个实例,如果在10秒内我们没有在第一个实例下按下任何按钮以释放互斥资源,那么在第二个实例中就会显示“Second instance is running!”,如第一种情况的运行结果所示。如果在10内我们在第一个实例中按下任何键以释放互斥资源,那么在第二个实例中就会显示“Running”,如第二种情况的运行结果所示。

三、使用SemaphoreSlim构造

  在这一小节中,我们将学习如何在SemaphoreSlim构造的帮助下,限制同时访问资源的线程数量。具体步骤如下所示:

1、使用Visual Studio 2015创建一个新的控制台应用程序。

2、双击打开“Program.cs”文件,编写代码如下所示:

  1. using System;
  2. using System.Threading;
  3. using static System.Console;
  4. using static System.Threading.Thread;
  5.  
  6. namespace Recipe03
  7. {
  8. class Program
  9. {
  10. static SemaphoreSlim semaphore = new SemaphoreSlim();
  11.  
  12. static void AccessDatabase(string name, int seconds)
  13. {
  14. WriteLine($"{name} waits to access a database");
  15. semaphore.Wait();
  16. WriteLine($"{name} was granted an access to a database");
  17. Sleep(TimeSpan.FromSeconds(seconds));
  18. WriteLine($"{name} is completed");
  19. semaphore.Release();
  20. }
  21.  
  22. static void Main(string[] args)
  23. {
  24. for(int i = ; i <= ; i++)
  25. {
  26. string threadName = "Thread" + i;
  27. int secondsToWait = + * i;
  28. var t = new Thread(() => AccessDatabase(threadName, secondsToWait));
  29. t.Start();
  30. }
  31. }
  32. }
  33. }

3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

  在第10行代码处,我们创建了一个SemaphoreSlim的实例,并对该构造方法传递了参数4,该参数指定了可以有多少个线程同时访问资源。然后,我们启动了6个不同名字的线程。每个线程都试着获取对数据库的访问,但是,我们限制了最多只有4个线程可以访问数据库,因此,当4个线程访问数据库后,其他2个线程必须等待,直到其他线程完成其工作后,调用“Release”方法释放资源之后才能访问数据库。

C#多线程之线程同步篇1的更多相关文章

  1. C#多线程之线程同步篇3

    在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...

  2. C#多线程之线程同步篇2

    在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...

  3. C#多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  4. 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)

    Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...

  5. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  6. C#多线程之线程池篇2

    在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...

  7. 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock

    [源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ...

  8. 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent

    [源码下载] 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEve ...

  9. IOS 多线程,线程同步的三种方式

    本文主要是讲述 IOS 多线程,线程同步的三种方式,更多IOS技术知识,请登陆疯狂软件教育官网. 一般情况下我们使用线程,在多个线程共同访问同一块资源.为保护线程资源的安全和线程访问的正确性. 在IO ...

随机推荐

  1. Emoji选项列表

    一.需要的前提文件 从网上下载Emoji的表情包,当然是png的图片,因为WPF不支持彩色的Emoji,所以,做列表的时候,需要用图片. 随着压缩包一起的还有一个Emoji.xml文件,文件的层级结构 ...

  2. Ngrok让你的本地Web应用暴露在公网上

    1.Ngrok介绍 Ngrok是一个反向代理,通过在公共的端点和本地运行的Web服务器之间建立一个安全的通道.Ngrok可捕获和分析所有通道上的流量,便于后期分析和重放.简单来说,利用 Ngrok可以 ...

  3. 获取微软原版“Windows 10 推送器(GWX)” 卸载工具

    背景: 随着Windows 10 免费更新的结束,针对之前提供推送通知的工具(以下简称GWX)来说使命已经结束,假设您还未将Windows 8.1 和Windows 7 更新到Windows 10 的 ...

  4. JS继承之原型继承

     许多OO语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.如前所述,由于函数没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支 ...

  5. C# 对象实例化 用json保存 泛型类 可以很方便的保存程序设置

    用于永久化对象,什么程序都行,依赖NewtonSoft.用于json序列化和反序列化. using Newtonsoft.Json; using System; using System.Collec ...

  6. [转载]SQL语句中的日期计算

    1. 本月的第一天SELECT  DATEADD(mm,  DATEDIFF(mm,0,getdate()),  0) 2. 本月的最后一天SELECT  dateadd(ms,-3,DATEADD( ...

  7. 【JS基础】对象

    delete 可以删除对象属性及变量 function fun(){ this.name = 'mm'; } var obj = new fun(); console.log(obj.name);// ...

  8. 简单Linux命令学习笔记

    1.查看进程 ps -ef | grep 关键字       /*关键字为服务名*/ netstat -unltp | grep 关键字        /*关键字为服务名或者是端口均可*/ 2.杀死进 ...

  9. 在同一个硬盘上安装多个 Linux 发行版及 Fedora 21 、Fedora 22 初体验

    在同一个硬盘上安装多个 Linux 发行版 以前对多个 Linux 发行版的折腾主要是在虚拟机上完成.我的桌面电脑性能比较强大,玩玩虚拟机没啥问题,但是笔记本电脑就不行了.要在我的笔记本电脑上折腾多个 ...

  10. 抛弃jQuery:Why?

    原文链接:http://blog.garstasio.com/you-dont-need-jquery/ 我的Blog:http://cabbit.me/you-dont-need-jquery/wh ...