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

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

 一、执行基本的原子操作

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

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

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

 using System;
using System.Threading;
using static System.Console; namespace Recipe01
{
abstract class CounterBase
{
public abstract void Increment(); public abstract void Decrement();
} class Counter : CounterBase
{
private int count; public int Count => count; public override void Increment()
{
count++;
} public override void Decrement()
{
count--;
}
} class CounterNoLock : CounterBase
{
private int count; public int Count => count; public override void Increment()
{
Interlocked.Increment(ref count);
} public override void Decrement()
{
Interlocked.Decrement(ref count);
}
} class Program
{
static void TestCounter(CounterBase c)
{
for (int i = ; i < ; i++)
{
c.Increment();
c.Decrement();
}
} static void Main(string[] args)
{
WriteLine("Incorrect counter"); var c1 = new Counter(); var t1 = new Thread(() => TestCounter(c1));
var t2 = new Thread(() => TestCounter(c1));
var t3 = new Thread(() => TestCounter(c1));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join(); WriteLine($"Total count: {c1.Count}");
WriteLine("--------------------------"); WriteLine("Correct counter"); var c2 = new CounterNoLock(); t1 = new Thread(() => TestCounter(c2));
t2 = new Thread(() => TestCounter(c2));
t3 = new Thread(() => TestCounter(c2));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join(); WriteLine($"Total count: {c2.Count}");
}
}
}

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”文件,编写代码如下所示:

 using System;
using System.Threading;
using static System.Console; namespace Recipe02
{
class Program
{
static void Main(string[] args)
{
const string MutexName = "Multithreading"; using (var m = new Mutex(false, MutexName))
{
// WaitOne方法的作用是阻止当前线程,直到收到其他实例释放的处理信号。
// 第一个参数是等待超时时间,第二个是否退出上下文同步域。
if (!m.WaitOne(TimeSpan.FromSeconds(), false))
{
WriteLine("Second instance is running!");
ReadLine();
}
else
{
WriteLine("Running!");
ReadLine();
// 释放互斥资源
m.ReleaseMutex();
}
} ReadLine();
}
}
}

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”文件,编写代码如下所示:

 using System;
using System.Threading;
using static System.Console;
using static System.Threading.Thread; namespace Recipe03
{
class Program
{
static SemaphoreSlim semaphore = new SemaphoreSlim(); static void AccessDatabase(string name, int seconds)
{
WriteLine($"{name} waits to access a database");
semaphore.Wait();
WriteLine($"{name} was granted an access to a database");
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine($"{name} is completed");
semaphore.Release();
} static void Main(string[] args)
{
for(int i = ; i <= ; i++)
{
string threadName = "Thread" + i;
int secondsToWait = + * i;
var t = new Thread(() => AccessDatabase(threadName, secondsToWait));
t.Start();
}
}
}
}

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. Oracle分析函数入门

    一.Oracle分析函数入门 分析函数是什么?分析函数是Oracle专门用于解决复杂报表统计需求的功能强大的函数,它可以在数据中进行分组然后计算基于组的某种统计值,并且每一组的每一行都可以返回一个统计 ...

  2. LeetCode-3LongestSubstringWithoutRepeatingCharacters(C#)

    # 题目 3. Longest Substring Without Repeating Characters Given a string, find the length of the longes ...

  3. [译]ZOOKEEPER RECIPES-Leader Election

    选主 使用ZooKeeper选主的一个简单方法是,在创建znode时使用Sequence和Ephemeral标志.主要思想是,使用一个znode,比如"/election",每个客 ...

  4. 探索ASP.NET MVC5系列之~~~3.视图篇(下)---包含常用表单和暴力解猜防御

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  5. C++随笔:.NET CoreCLR之GC探索(3)

    有几天没写GC相关的文章了哈,今天我讲GC的方式是通过一个小的Sample来讲解,这个小的示例代码只有全部Build成功了才会有.地址为D:\coreclr2\coreclr\bin\obj\Wind ...

  6. 声音分贝的概念,dBSPL.dBm,dBu,dBV,dBFS

    需要做个音频的PPM表,看着一堆的音频术语真是懵了,苦苦在网上扒了几天的文档,终于有了点收获,下面关于声音的分贝做个总结. 分贝 Decibel 分贝(dB)是一个对数单位(logarithmic u ...

  7. ASP.NET Core 中文文档 第四章 MVC(4.6)Areas(区域)

    原文:Areas 作者:Dhananjay Kumar 和 Rick Anderson 翻译:耿晓亮(Blue) 校对:许登洋(Seay) Areas 是 ASP.NET MVC 用来将相关功能组织成 ...

  8. C# 数组的交集、差集、并集

    C# 数组的交集.差集.并集 工作中经常会用这方面的知识来检查那些字段是必须输入的,那些是禁止输入. using System; using System.Collections.Generic; u ...

  9. C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决

    返回目录 关于死锁的原因 理解该死锁的原因在于理解await 处理contexts的方式,默认的,当一个未完成的Task 被await的时候,当前的上下文将在该Task完成的时候重新获得并继续执行剩余 ...

  10. JDBC增加删除修改

    一.配置程序--让我们程序能找到数据库的驱动jar包 1.把.jar文件复制到项目中去,整合的时候方便. 2.在eclipse项目右击"构建路径"--"配置构建路径&qu ...