C#当中的多线程_2

第2章 线程同步

原来以为线程同步就是lock,monitor等呢,看了第二章真是大开眼界啊!

第一章中我们遇到了一个叫做竞争条件的问题。引起的原因是没有进行正确的线程同步。当一个线程在执行操作时候,其他的线程需要依次等待。这样的问题通常被称为线程同步。

有多种方式来进行线程的同步。

第一:首先线程同步的原因是,多线程访问共享对象,如果可以通过重新设计程序来移除共享状态,从而去掉复杂的同步构造。

第二:使用原子操作,所谓原子操作就是一个操作只占用一个量子时间,一次就可以完成。所以只有当前操作结束之后,其他线程才能执行其他操作。这时无需实现其他线程等待当前操作完成,这就避免了使用锁,也就排除了死锁的可能。

第三:上面两者不可行,我们就要采用一些方式来协调线程。方法就是通过将线程置为阻塞状态,当线程置为阻塞状态的时候,此时线程只会占用很少的cpu时间,但是会引入至少一次的上下文切换。上下文切换是指操作系统的线程调度器。该调度器会保存等待线程的状态,并切换到另一个线程,依次恢复等待的线程的状态。每一次线程间的切换是非常消耗资源的。但是如果线程会挂起很长时间,这么做是值得的。这种方式叫做内核模式,因为只有内核才能阻止线程占用cpu时间。

如果线程只需要等待一小段时间,最好只是简单的等待,而不用将线程切换到阻塞状态。虽然线程等待时候回浪费CPU时间,但是这样却节省了上下文切换耗费的CPU时间。该方式被称为用户模式。这样的方式很轻量,速度很快,如果线程需要等待很长时间,则会浪费大量CPU时间。

为了缓解两种方式的问题,可以采用混合模式。混合模式先尝试用用户模式等待,超过一定时间限制,转为内核模式进入阻塞状态。

①原子操作

使用Interlocked类,这个类中提供一些Increment,Decrement和Add等基本的数学操作的原子方法。从而帮助我们在编写一些代码时候,无需使用锁。

例如:下面的例子,我们定义了两个计数的方法,分别用于自增和自减,区别在于第一次没有使用原子操作,第二次使用了原子操作。结果可以看出来,使用原子操作的结果是0,实现了线程间的同步。

1 using System;

2 using System.Threading;

3

4 namespace Chapter2.Recipe1

5 {

6     internal class Program

7     {

8         private static void Main(string[] args)

9         {

10             Console.WriteLine("Incorrect counter");

11

12             var c = new Counter();

13

14             var t1 = new Thread(() => TestCounter(c));

15             var t2 = new Thread(() => TestCounter(c));

16             var t3 = new Thread(() => TestCounter(c));

17             t1.Start();

18             t2.Start();

19             t3.Start();

20             t1.Join();

21             t2.Join();

22             t3.Join();

23

24             Console.WriteLine("Total count: {0}", c.Count);

25             Console.WriteLine("--------------------------");

26

27             Console.WriteLine("Correct counter");

28

29             var c1 = new CounterNoLock();

30

31             t1 = new Thread(() => TestCounter(c1));

32             t2 = new Thread(() => TestCounter(c1));

33             t3 = new Thread(() => TestCounter(c1));

34             t1.Start();

35             t2.Start();

36             t3.Start();

37             t1.Join();

38             t2.Join();

39             t3.Join();

40

41             Console.WriteLine("Total count: {0}", c1.Count);

42         }

43

44         static void TestCounter(CounterBase c)

45         {

46             for (int i = 0; i < 100000; i++)

47             {

48                 c.Increment();

49                 c.Decrement();

50             }

51         }

52

53         class Counter : CounterBase

54         {

55             private int _count;

56

57             public int Count { get { return _count; } }

58

59             public override void Increment()

60             {

61                 _count++;

62             }

63

64             public override void Decrement()

65             {

66                 _count--;

67             }

68         }

69

70         class CounterNoLock : CounterBase

71         {

72             private int _count;

73

74             public int Count { get { return _count; } }

75

76             public override void Increment()

77             {

78                 Interlocked.Increment(ref _count);

79             }

80

81             public override void Decrement()

82             {

83                 Interlocked.Decrement(ref _count);

84             }

85         }

86

87         abstract class CounterBase

88         {

89             public abstract void Increment();

90

91             public abstract void Decrement();

92         }

93     }

94 }

②使用Mutex类

互斥锁(Mutex)

互斥锁是一个互斥的同步对象,意味着同一时间有且仅有一个线程可以获取它。

互斥锁可适用于一个共享资源每次只能被一个线程访问的情况。

1 using System;

2 using System.Threading;

3

4 namespace Chapter2.Recipe2

5 {

6     class Program

7     {

8         static void Main(string[] args)

9         {

10             const string MutexName = "CSharpThreadingCookbook";

11

12             using (var m = new Mutex(false, MutexName))

13             {

14                 if (!m.WaitOne(TimeSpan.FromSeconds(5), false))

15                 {

16                     Console.WriteLine("Second instance is running!");

17                 }

18                 else

19                 {

20                     Console.WriteLine("Running!");

21                     Console.ReadLine();

22                     m.ReleaseMutex();

23                 }

24             }

25         }

26     }

27 }

Mutex在什么地方获取,在什么地方释放,这个要记住。

一般是在委托的方法中使用,先获得,在执行操作代码,然后释放掉mutex量

1 class Program

2         {

3                 static Mutex mu = new Mutex();

4                 static void Main(string[] args)

5                 {

6                         Thread1();

7                         Thread2();

8                         Console.ReadKey();

9                 }

10

11                 static void Count()

12                 {

13                         mu.WaitOne();

14                         for (int i = 0; i < 10; i++)

15                         {

16                                 Console.WriteLine("{0} is writing {1}",

17                                                            Thread.CurrentThread.Name, i.ToString());

18                         }

19                         mu.ReleaseMutex();

20                 }

21

22                 static void Thread1()

23                 {

24                         Thread thread1 = new Thread(Count);

25                         thread1.Name = "Thread1";

26                         thread1.Start();

27                 }

28

29                 static void Thread2()

30                 {

31                         Thread thread2 = new Thread(Count);

32                         thread2.Name = "Thread2";

33                         thread2.Start();

34                 }

35         }

上面的例子,是比较简单的例子。

如果不使用Mutex的话,把上面Mutex注释掉

③使用SemaphoreSlim类

它是Semaphore的轻量级版本。

1     static void Main(string[] args)

2         {

3             for (int i = 1; i <= 6; i++)

4             {

5                 string threadName = "Thread " + i;

6                 int secondsToWait = 2 + 2 * i;

7                 var t = new Thread(() => AccessDatabase(threadName, secondsToWait));

8                 t.Start();

9             }

10         }

11

12         static SemaphoreSlim _semaphore = new SemaphoreSlim(4);

13

14         static void AccessDatabase(string name, int seconds)

15         {

16             Console.WriteLine("{0} waits to access a database", name);

17             _semaphore.Wait();

18             Console.WriteLine("{0} was granted an access to a database", name);

19             Thread.Sleep(TimeSpan.FromSeconds(seconds));

20             Console.WriteLine("{0} is completed", name);

21             _semaphore.Release();

22

23         }

24     }

1、程序一启动就生成一个限制了线程并发数的SemaphoreSlim实例,限制其并发数目为4个

2、启动了六个线程,每个线程拥有不同的线程执行时间。

3、执行程序过程中,我们发现,最大并发数真的就是4个,如下图所示

首先2、4、1、3都获得了权限,5、6就必须等待,当1执行完成释放信号量,6才获得权限。

线程间的通信!

在讨论这个问题之前,我们先了解这样一种观点,线程之间的通信是通过发信号来进行沟通的。

③使用AutoResetEvent

AutoResetEvent类来从一个线程向另外一个线程发送通知。AutoResetEvent类可以通知等待的线程有某事件发生。

1 namespace Recipe4_2

2 {

3         class Program

4         {

5                 static AutoResetEvent myAutoReset = new AutoResetEvent(false);

6                 static void Main(string[] args)

7                 {

8                         Thread threadA = new Thread(FunctionA);

9                         threadA.Name = "ThreadA";

10                       Thread threadB = new Thread(FunctionB);

11                        threadB.Name = "ThreadB";

12                        threadA.Start();

13                        threadB.Start();

14                 }

15

16                 static void FunctionA()

17                 {

18                         for (int i = 0; i <= 10; i++)

19                         {

20                                 Console.WriteLine("This is ThreadA {0}",i);

21                                 Thread.Sleep(500);

22                         }

23                         myAutoReset.Set();

24                 }

25

26                 static void FunctionB()

27                 {

28                         myAutoReset.WaitOne();

29                         for (int i = 0; i <= 10; i++)

30                         {

31                                 Console.WriteLine("This is ThreadB {0}", i);

32                                 Thread.Sleep(500);

33                         }

34                         myAutoReset.Set();

35                 }

36         }

37 }

上例中,程序启动创建了一个AutoResetEvent实例,并赋初始值为false,这个false定义了这个AutoResetEvent的实例的初始状态是unsignaled状态。这意味着我们调用这个实例的WaitOne方法将会被阻塞,直到我们调用set方法。如果初始值为true,那么AutoResetEvent实例的状态为signaled,如果调用WaitOne则立即被处理,然后事件状态立即转为unsignaled。

AutoResetEvent采用的是内核时间模式,所以时间不能太长。如果需要处理长时间的操作,那么使用ManualResetEventSlim类更好。

④使用ManualResetEventSlim类

1     class Program

2     {

3         static void Main(string[] args)

4         {

5             var t1 = new Thread(() => TravelThroughGates("Thread 1", 5));

6             var t2 = new Thread(() => TravelThroughGates("Thread 2", 6));

7             var t3 = new Thread(() => TravelThroughGates("Thread 3", 12));

8             t1.Start();

9              t2.Start();

10            t3.Start();

11             Thread.Sleep(TimeSpan.FromSeconds(6));

12             Console.WriteLine("The gates are now open!");

13             _mainEvent.Set();

14             Thread.Sleep(TimeSpan.FromSeconds(2));   //大门打开两秒钟关闭

15             _mainEvent.Reset();

16             Console.WriteLine("The gates have been closed!");

17             Thread.Sleep(TimeSpan.FromSeconds(10));

18             Console.WriteLine("The gates are now open for the second time!");

19             _mainEvent.Set();

20             Thread.Sleep(TimeSpan.FromSeconds(2));

21             Console.WriteLine("The gates have been closed!");

22             _mainEvent.Reset();

23         }

24

25         static void TravelThroughGates(string threadName, int seconds)

26         {

27             Console.WriteLine("{0} falls to sleep", threadName);

28             Thread.Sleep(TimeSpan.FromSeconds(seconds));

29             Console.WriteLine("{0} waits for the gates to open!", threadName);

30             _mainEvent.Wait();

31             Console.WriteLine("{0} enters the gates!", threadName);

32         }

33

34         static ManualResetEventSlim _mainEvent = new ManualResetEventSlim(false);

35     }

AutoResetEvent这个类有点像旋转门,一次只允许一个人通过。ManualResetEventSlim是ManualResetEvent的混合版本,像一个手动开关的大门,一直保持大门的敞开直到调用Reset方法。当调用set方法的时候,相当于打开了大门从而允许准备好的线程接收信号并继续工作。没有准备好的线程,没有赶上大门打开的时间。调用Reset方法相当于关闭了大门。

⑤使用CountDownEvent类

使用CountDownEvent信号类来等待一定数量的操作完成。

例子1:

1     class Program

2     {

3         static void Main(string[] args)

4         {

5             Console.WriteLine("Starting two operations");

6             var t1 = new Thread(() => PerformOperation("Operation 1 is completed", 4));

7             var t2 = new Thread(() => PerformOperation("Operation 2 is completed", 8));

8             t1.Start();

9             t2.Start();

10             _countdown.Wait();

11             Console.WriteLine("Both operations have been completed.");

12             _countdown.Dispose();

13         }

14

15         static CountdownEvent _countdown = new CountdownEvent(2);//初始就定义为2

16

17         static void PerformOperation(string message, int seconds)

18         {

19             Thread.Sleep(TimeSpan.FromSeconds(seconds));

20             Console.WriteLine(message);

21             _countdown.Signal();

22         }

23     }

CountdownEvent.Signal 方法


名称


说明


Signal()


向 CountdownEvent 注册信号,同时减小 CurrentCount 的值。


Signal(Int32)


向 CountdownEvent 注册多个信号,同时将 CurrentCount 的值减少指定数量。


Wait()


阻止当前线程,直到设置了 CountdownEvent 为止。


Dispose()


释放 CountdownEvent 类的当前实例所使用的所有资源。

缺点:如果调用Signal()没有达到指定的次数,那么Wait就会一直等待下去。所以每次线程使用结束之后都要调用一次Signal()方法。

例子2:

1 using System;

2 using System.Collections.Generic;

3 using System.Linq;

4 using System.Text;

5 using System.Threading;

6

7 namespace CountdownEventDemo

8 {

9         class Program

10         {

11                 static void Main(string[] args)

12                 {

13                         var customers = Enumerable.Range(1, 20);

14

15                         using (var countdown = new CountdownEvent(1))

16                         {

17                                 foreach (var customer in customers)

18                                 {

19                                         int currentCustomer = customer;

20                                         ThreadPool.QueueUserWorkItem(delegate

21                                         {

22                                                 BuySomeStuff(currentCustomer);

23                                                 countdown.Signal();

24                                         });

25                                         countdown.AddCount();

26                                 }

27

28                                 countdown.Signal();

29                                 countdown.Wait();

30                         }

31

32                         Console.WriteLine("All Customers finished shopping...");

33                         Console.ReadKey();

34                 }

35

36                 static void BuySomeStuff(int customer)

37                 {

38                         // Fake work

39                         Thread.SpinWait(200000000);

40

41                         Console.WriteLine("Customer {0} finished", customer);

42                 }

43         }

44 }

http://www.cnblogs.com/shanyou/archive/2009/10/27/1590890.html


AddCount()


将 CountdownEvent 的当前计数加 1。


AddCount(Int32)


将 CountdownEvent 的当前计数增加指定值。

⑥使用Barrier类

Barrier类用于组织多个线程及时在某个时刻碰面。其提供了一个回调函数,每次线程调用了SignalAndWait方法后该回调函数会被执行。

1     class Program

2     {

3         static void Main(string[] args)

4         {

5             var t1 = new Thread(() => PlayMusic("the guitarist", "play an amazing solo", 5));

6             var t2 = new Thread(() => PlayMusic("the singer", "sing his song", 2));

7

8             t1.Start();

9             t2.Start();

10         }

11

12         static Barrier _barrier = new Barrier(2,

13      b => Console.WriteLine("End of phase {0}", b.CurrentPhaseNumber + 1));

14

15         static void PlayMusic(string name, string message, int seconds)

16         {

17             for (int i = 1; i < 3; i++)

18             {

19                 Console.WriteLine("----------------------------------------------");

20                 Thread.Sleep(TimeSpan.FromSeconds(seconds));

21                 Console.WriteLine("{0} starts to {1}", name, message);

22                 Thread.Sleep(TimeSpan.FromSeconds(seconds));

23                 Console.WriteLine("{0} finishes to {1}", name, message);

24                 _barrier.SignalAndWait();

25             }

26         }

27     }

⑦使用ReaderWriterLockSilm类

这个类用来创建一个线程安全机制,在多线程中对一个集合进行读写操作。ReaderWriterLockSilm类代表了一个管理资源访问的锁,允许多个线程同时进行读取以及独占写。

下例中定义了三个读线程和两个写线程,读没什么好讲的,就是判断数据在不在集合里,在的话就读出来,不在就释放读锁。写操作是先创建一个新key,获取一下更新锁,判断新key在不在集合当中,不在的话,升级更新锁变成写锁,写入数据,然后释放写锁,延时,释放更新锁。

1     class Program

2     {

3         static void Main(string[] args)

4         {

5             new Thread(Read){ IsBackground = true }.Start();

6             new Thread(Read){ IsBackground = true }.Start();

7             new Thread(Read){ IsBackground = true }.Start();

8

9             new Thread(() => Write("Thread 1")){ IsBackground = true }.Start();

10           new Thread(() => Write("Thread 2")){ IsBackground = true }.Start();

11

12           Thread.Sleep(TimeSpan.FromSeconds(30));

13         }

14

15         static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();

16         static Dictionary<int, int> _items = new Dictionary<int, int>();

17

18         static void Read()

19         {

20             Console.WriteLine("Reading contents of a dictionary");

21             while (true)

22             {

23                 try

24                 {

25                     _rw.EnterReadLock();

26                     foreach (var key in _items.Keys)

27                     {

28                         Thread.Sleep(TimeSpan.FromSeconds(0.1));

29                     }

30                 }

31                 finally

32                 {

33                     _rw.ExitReadLock();

34                 }

35             }

36         }

37

38         static void Write(string threadName)

39         {

40             while (true)

41             {

42                 try

43                 {

44                     int newKey = new Random().Next(250);

45                     _rw.EnterUpgradeableReadLock();

46                     if (!_items.ContainsKey(newKey))

47                     {

48                         try

49                         {

50                             _rw.EnterWriteLock();

51                             _items[newKey] = 1;

52                             Console.WriteLine("New key {0} is added to a dictionary by a {1}", newKey, threadName);

53                         }

54                         finally

55                         {

56                             _rw.ExitWriteLock();

57                         }

58                     }

59                     Thread.Sleep(TimeSpan.FromSeconds(0.1));

60                 }

61                 finally

62                 {

63                     _rw.ExitUpgradeableReadLock();

64                 }

65             }

66         }

67     }

启动了三个读线程和两个写线程,ReaderWriterLockSlim类有两种锁,读锁和写锁。读锁允许多线程进行数据读取,写锁再被释放之前会阻塞其他线程的所有操作。

有一种场景是这样的,当我们需要根据当前读取的数据进行判读是否需要进行修改的时候,如果这时获取写锁,就会阻塞所有阅读者的权限,从而浪费大量的时间。为了减少阻塞的时间,可以使用EnterUpgradeableReadLock和ExitUpgradeaReadLock方法。这时当我们获得读锁进行数据读取的时候,如果发现必须要修改,只需要使用EnterWriteLock方法进行升级锁,然后快速一次写操作,最后用ExitWriteLock释放写锁。

看了一下有一个地方没明白,就是EnterUpgradeableReadLock和EnterReadLock他俩的区别,感觉很像!上网查了一下,大概明白了。

可更新锁:

再一个原子操作里将读锁升级为写锁是很有用的,例如,假设你想要再一个list 里面写一些不存在的项的时候,你可能会执行下面的一些步骤:

1获取一个读锁。

2测试,如果要写的东西在列表中,那么释放锁,然后返回。

3释放读锁。

4获取一个写锁

5添加项,写东西,

6释放写锁。

问题是:在第三步和第四步之间,可能有另一个线程修改了列表。

ReaderWriterLockSlim 通过一个叫做可更新锁( upgradeable lock),来解决这个问题。

一个可更新锁除了它可以在一个原子操作中变成写锁外很像一个读锁,你可以这样使用它:

调用EnterUpgradeableReadLock 获取可更新锁。执行一些读操作,例如判断要写的东西在不在List中。调用EnterWriteLock , 这个方法会将可更新锁 升级为 写锁。执行写操作,调用ExitWriteLock 方法,这个方法将写锁转换回可更新锁。继续执行一些读操作,或什么都不做。

调用ExitUpgradeableReadLock 释放可更新锁。

从调用者的角度来看,它很像一个嵌套/递归锁,从功能上讲,在第三步,

ReaderWriterLockSlim 在一个原子操作里面释放读锁,然后获取写锁。

可更新锁和读锁的重要区别是:尽管可更新锁可以和读锁共存,但是一次只能有一个可更新锁被获取。这样的主要目的是防止死锁。

貼り付け元  <http://www.jb51.net/article/36798.htm>

⑧使用SpinWait类

这个东西是利用混合模式来让线程进行等待,可以类比于Thread.Sleep(),感觉书上的Demo虽然解释起来还算方便,但是效果真的不好观察,于是我就在网上查一个文章,感觉总结的还挺不错。

http://www.it165.net/pro/html/201304/5544.html

时间: 2024-11-09 16:40:27

C#当中的多线程_2的相关文章

C#当中的多线程_任务并行库(中)

发现自己有点懒了!也可能是越往后越难了,看书理解起来有点费劲,所以这两天就每天更新一点学习笔记吧. 4.5 将APM模式转化为任务 书上提供的三种方式 方式一: 1 class Program 2         { 3                 //定义一个委托 4                 private delegate string AsynchronousTask(string threadName); 5 6                 static void Mai

C#当中的多线程_任务并行库(下)

4.8 处理任务中的异常 下面这个例子讨论了任务当中抛出异常,以及任务异常的获取 1     class Program 2     { 3         static void Main(string[] args) 4         { 5             //声明一个任务 6             Task<int> task; 7             //第一种方式,普通的try...catch捕获异常 8             try 9             

Android Learning:多线程与异步消息处理机制

在最近学习Android项目源码的过程中,遇到了很多多线程以及异步消息处理的机制.由于之前对这块的知识只是浅尝辄止,并没有系统的理解.但是工程中反复出现让我意识到这个知识的重要性.所以我整理出这篇博客,主要介绍了线程和异步处理机制的意义和用法,目的在于帮助初学者能够加深对异步消息处理机制的理解,在实际Android工程中能够更多地使用AsyncTask工具类在子线程中进行UI更新. 一.Android当中的多线程[1] 在Android当中,当一个应用程序的组件启动的时候,并且没有其他的应用程序

Android 多线程-----AsyncTask详解

本篇随笔将讲解一下Android的多线程的知识,以及如何通过AsyncTask机制来实现线程之间的通信. 一.Android当中的多线程 在Android当中,当一个应用程序的组件启动的时候,并且没有其他的应用程序组件在运行时,Android系统就会为该应用程序组件开辟一个新的线程来执行.默认的情况下,在一个相同Android应用程序当中,其里面的组件都是运行在同一个线程里面的,这个线程我们称之为Main线程.当我们通过某个组件来启动另一个组件的时候,这个时候默认都是在同一个线程当中完成的.当然

Python并发编程之创建多线程的几种方法(二)

大家好,并发编程 进入第二篇. 今天的内容会比较基础,主要是为了让新手也能无障碍地阅读,所以还是要再巩固下基础.学完了基础,你们也就能很顺畅地跟着我的思路理解以后的文章. 本文目录 学会使用函数创建多线程 学会使用类创建多线程 多线程:必学函数讲解 经过总结,Python创建多线程主要有如下两种方法: 函数 类 接下来,我们就来揭开多线程的神秘面纱. . 学会使用函数创建多线程 在Python3中,Python提供了一个内置模块 threading.Thread,可以很方便地让我们创建多线程.

android面试题目

最近才开的博客,希望大家多多关注,andorid开发也做了3年有余了,也面试多家企业,借此机会分享一下,我们中遇到过的问题以及解决方案吧,希望能够对正在找工作的andoird程序员有一定的帮助.学完<andorid从零开始教程>+面试题目全理解,年薪20w以上绝对没问题. 特别献上整理过的50道面试题目 1.listView的优化方式 重用convertView viewHolder static class viewHolder 在列表里面有图片的情况下,监听滑动不加载图片 多个不同布局,可

JavaSE中线程与并行API框架学习笔记1——线程是什么?

前言:虽然工作了三年,但是几乎没有使用到多线程之类的内容.这其实是工作与学习的矛盾.我们在公司上班,很多时候都只是在处理业务代码,很少接触底层技术. 可是你不可能一辈子都写业务代码,而且跳槽之后新单位很可能有更高的技术要求.除了干巴巴地翻书,我们可以通过两个方式来解决这个问题:一是做业余项目,例如在github上传自己的demo,可以实际使用:二是把自己的学习心得写成博客,跟同行们互相交流. 3.1 线程的初窥门径 我们在之前的文章里提到的程序其实都是单线程程序,也就说启动的程序从main()程

JVM学习与问题总结——java内存区域与内存溢出异常

java虚拟机将内存分为哪些区域? 根据Java SE7版本的Java虚拟机规范,虚拟机管理的内存包括5个运行时数据区域: 程序计数器 虚拟机栈 本地方法栈 方法区 堆 运行时数据区各部分的作用? 程序计数器 一个线程所执行的字节码的行号指示器. 字节码解释器会通过改变计数器的值来选取下一条将要执行的指令,那么分支.循环.跳转.异常处理.线程恢复都需要依赖计数器来完成.而Java虚拟机当中的多线程是通过争取CPU时间片来切换线程执行任务,当一个线程重新获取CPU时间片的时候就需要恢复上一次任务执

Java多线程当中的violate

violate在多线程当中的用来修饰某个变量,这个变量只有一份,也就是不存在多线程cache它们本地的情况出现.从而保证对他的读和写是唯一的,不会存在不一致的情况. 一.适用的情况 对于某个变量只有读和写两种单一操作. violate int count; count=1; count=0; if(count==0){...} 上面都是单一操作,而 count=cout+1; count++; 不是单一读写操作,因为在上面两种操作当中都涉及了先读然后在写的操作过程,这样volate无法保证原子性