1.1 简介

为了防止一个应用程序控制CPU而导致其他应用程序和操作系统本身永远被挂起这一可能情况,操作系统不得不使用某种方式将物理计算分割为一些虚拟的进程,并给予每个执行程序一定量的计算能力。此外操作系统必须始终能够优先访问CPU,并能调整不同程序访问CPU的优先级。线程正式这一慨念的实现。

多线程优点:可以同时执行多个计算任务,有可能提高计算机的处理能力,使得计算机每秒能执行越来越多的命令

多线程缺点:消耗大量的操作系统资源。多个线程共享一个处理器将导致操作系统忙于管理这些线程,而无法运行程序。

1.2 创建线程

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Thread t1 = new Thread(new ThreadStart(PrintNumbers));//无参数的委托
            t1.Start();Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有参数的委托t2.Start(10);Console.ReadLine();}static void PrintNumbers(){Console.WriteLine("Starting...");for (int i = 0; i < 10; i++){Console.WriteLine(i);}}//注意:要使用ParameterizedThreadStart,定义的参数必须为objectstatic void PrintNumbers(object count){Console.WriteLine("Starting...");for (int i = 0; i < Convert.ToInt32(count); i++){Console.WriteLine(i);}}}
}

注释:我们只需指定在不同线程运行的方法名,而C#编译器会在后台创建这些对象

1.3 暂停线程

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Thread t1 = new Thread(PrintNumbersWithDelay);t1.Start();PrintNumbers();Console.ReadLine();}static void PrintNumbers(){Console.WriteLine("Starting...");for (int i = 0; i < 10; i++){Console.WriteLine(i);}}static void PrintNumbersWithDelay(){Console.WriteLine("Starting...");for (int i = 0; i < 10; i++){Thread.Sleep(TimeSpan.FromSeconds(2));Console.WriteLine(i);}}}
}

注释:使用Thread.Sleep(TimeSpan.FromSeconds(2));暂停线程

1.4 线程等待

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Console.WriteLine("Starting...");Thread t = new Thread(PrintNumbersWithDelay);t.Start();t.Join();   //使用Join等待t完成
            PrintNumbers();Console.WriteLine("THread Complete");Console.ReadLine();}static void PrintNumbers(){Console.WriteLine("Starting...");for (int i = 0; i < 10; i++){Console.WriteLine(i);}}static void PrintNumbersWithDelay(){Console.WriteLine("Starting...");for (int i = 0; i < 10; i++){Thread.Sleep(TimeSpan.FromSeconds(2));Console.WriteLine(i);}}}
}

注释:使用t.Join();   等待t完成

1.5 终止线程

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Console.WriteLine("Starting Program...");Thread t1 = new Thread(PrintNumbersWithDelay);t1.Start();Thread.Sleep(TimeSpan.FromSeconds(6));t1.Abort();    //使用Abort()终止线程Console.WriteLine("Thread t1 has been aborted");Thread t2 = new Thread(PrintNumbers);PrintNumbers();Console.ReadLine();}static void PrintNumbers(){Console.WriteLine("Starting...");for (int i = 0; i < 10; i++){Console.WriteLine(i);}}static void PrintNumbersWithDelay(){Console.WriteLine("Starting...");for (int i = 0; i < 10; i++){Thread.Sleep(TimeSpan.FromSeconds(2));Console.WriteLine(i);}}}
}

注释:使用Thread实例的Abort方法终止线程

1.6 检测线程状态

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Console.WriteLine("Start Program...");Thread t1 = new Thread(PrintNumbersWithStatus);Thread t2 = new Thread(DoNothing);Console.WriteLine(t1.ThreadState.ToString());//获取实例线程状态
            t2.Start();t1.Start();for (int i = 0; i < 30; i++){Console.WriteLine(t1.ThreadState.ToString());}Thread.Sleep(TimeSpan.FromSeconds(6));t1.Abort();Console.WriteLine("thread t1 has been aborted");Console.WriteLine(t1.ThreadState.ToString());Console.WriteLine(t2.ThreadState.ToString());Console.ReadLine();}private static void PrintNumbersWithStatus(){Console.WriteLine("Starting...");Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());//获取当前线程状态for (int i = 0; i < 10; i++){Thread.Sleep(TimeSpan.FromSeconds(2));Console.WriteLine(i);}}private static void DoNothing(){Thread.Sleep(TimeSpan.FromSeconds(2));}}
}

注释:使用Thread.ThreadState获取线程的运行状态。ThreadState是一个C#枚举。谨记:不要在程序中使用线程终止,否则可能会出现意想不到的结果

1.7 线程优先级

using System;
using System.Diagnostics;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Console.WriteLine($"Current thread priority: {Thread.CurrentThread.Priority}");Console.WriteLine("Running on all cores available");//获取实例线程状态
            RunThreads();Thread.Sleep(TimeSpan.FromSeconds(2));Console.WriteLine("Running on a single Core");//让操作系统的所有线程运行在单个CPU核心上Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);RunThreads();Console.ReadLine();}private static void RunThreads(){var sample = new ThreadSample();var t1 = new Thread(sample.CountNumbers);t1.Name = "Thread One";var t2 = new Thread(sample.CountNumbers);t2.Name = "Thread Two";t1.Priority = ThreadPriority.Highest;//使用Priority设置线程的优先级t2.Priority = ThreadPriority.Lowest;t1.Start();t2.Start();Thread.Sleep(TimeSpan.FromSeconds(2));sample.Stop();}}class ThreadSample{private bool _isStopped = false;public void Stop(){_isStopped = true;}public void CountNumbers(){long counter = 0;while (!_isStopped){counter++;}Console.WriteLine($"{Thread.CurrentThread.Name} with {Thread.CurrentThread.Priority} priority has a count={counter.ToString("N0")}");}}
}

注释:单核执行多线程耗费的时间比多核的多很多

1.8 前台线程和后台线程

using System;
using System.Diagnostics;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){var sampleForground = new ThreadSample(10);var sampleBackground = new ThreadSample(20);var t1 = new Thread(sampleForground.CountNumbers);t1.Name = "ForegroundThread";   //没有明确声明的均为前台线程var t2 = new Thread(sampleBackground.CountNumbers);t2.Name = "BackgroundThread";t2.IsBackground = true;    //设置为后台线程
t1.Start();t2.Start();}}class ThreadSample{private readonly int _iteration;public ThreadSample(int iteration){_iteration = iteration;}public void CountNumbers(){for (int i = 0; i < _iteration; i++){Thread.Sleep(TimeSpan.FromSeconds(0.5));Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");}}}
}

注释:进程会等待所有的前台线程完成后再结束工作,但是如果只剩下后台线程,则会直接结束工作

1.9 向线程传递参数

using System;
using System.Diagnostics;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){ThreadSample sample = new ThreadSample(5);Thread t1 = new Thread(sample.CountNumbers);t1.Name = "ThreadOne";t1.Start();t1.Join();Console.WriteLine("--------------------------");Thread t2 = new Thread(Count);t2.Name = "ThreadTwo";t2.Start(3);t2.Join();Console.WriteLine("--------------------------");//使用lambda表达式引用另一个C#对方的方式被称为闭包。当在lambda表达式中使用任何局部变量时,C#会生成一个类,并将该变量作为该类的一个属性,但是我们无须定义该类,C#编译器会自动帮我们实现Thread t3 = new Thread(()=> CountNumbers(5));t3.Name = "ThreadThree";t3.Start();t3.Join();Console.WriteLine("--------------------------");int i = 10;Thread t4 = new Thread(() => PrintNumber(i));i = 20;Thread t5 = new Thread(() => PrintNumber(i));t4.Start();t5.Start();//t4, t5都会输出20, 因为t4,t5没有Start之前i已经变成20了
            Console.ReadKey();}static void Count(object iterations){CountNumbers((int)iterations);}static void CountNumbers(int iterations){for (int i = 1; i <= iterations; i++){Thread.Sleep(TimeSpan.FromSeconds(0.5));Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");}}static void PrintNumber(int number){Console.WriteLine(number);}}class ThreadSample{private readonly int _iteration;public ThreadSample(int iteration){_iteration = iteration;}public void CountNumbers(){for (int i = 1; i <= _iteration; i++){Thread.Sleep(TimeSpan.FromSeconds(0.5));Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");}}}
}

注释:也可以使用ThreadStart传递参数

1.10 使用C# lock关键字

using System;
using System.Diagnostics;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Console.WriteLine("Incorrect Counter");Counter 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();Console.WriteLine($"Total Count: {c1.Count}");Console.WriteLine("------------------------");Console.WriteLine("Correct counter");CounterWithLock c2 = new CounterWithLock();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();Console.WriteLine($"Total count:{c2.Count}");Console.ReadLine();}static void TestCounter(CounterBase c){for (int i = 0; i < 100000; i++){c.Increment();c.Decrement();}}class Counter : CounterBase{public int Count { get; private set; }public override void Decrement(){Count--;}public override void Increment(){Count++;}}class CounterWithLock : CounterBase{private readonly object _asyncRoot = new object();public int Count { get; private set; }public override void Decrement(){lock (_asyncRoot){Count--;}}public override void Increment(){lock (_asyncRoot){Count++;}}}abstract class CounterBase{public abstract void Increment();public abstract void Decrement();}}class ThreadSample{private readonly int _iteration;public ThreadSample(int iteration){_iteration = iteration;}public void CountNumbers(){for (int i = 1; i <= _iteration; i++){Thread.Sleep(TimeSpan.FromSeconds(0.5));Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");}}}
}

注释:不加锁,得出的结果不确定,竞争条件下很容易出错。加锁得出的结果是正确的,但是性能受到了影响

1.11 使用Monitor类锁定资源

using System;
using System.Diagnostics;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){object lock1 = new object();object lock2 = new object();new Thread(() => LockTooMuch(lock1, lock2)).Start();lock (lock2){Thread.Sleep(1000);Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");//直接使用Monitor.TryEnter, 如果在第二个参数之前还未获取到lock保护的资源会返回falseif (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5))){Console.WriteLine("Acquired a protected resource successfully");}else{Console.WriteLine("Timeout acquiring a resource");}}new Thread(() => LockTooMuch(lock1, lock2)).Start();Console.WriteLine("-----------------------------");/* 下面代码会造成死锁, 所以注释掉lock (lock2){Console.WriteLine("This will be a deadlock!");Thread.Sleep(1000);lock (lock1){Console.WriteLine("Acquired a protected resource successfully");}}*/}static void LockTooMuch(object lock1, object lock2){lock (lock1){Thread.Sleep(1000);lock (lock2);}}}
}

注释:Monitor.TryEnter在指定的时间内尝试获取指定对象上的排他锁

1.12 处理异常

using System;
using System.Diagnostics;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Thread t = new Thread(FaultyThread);t.Start();t.Join();try{t = new Thread(BadFaultyThread);t.Start();}catch (Exception ex){Console.WriteLine("We won't get here");}}static void BadFaultyThread(){Console.WriteLine("Starting a faulty thread.....");Thread.Sleep(TimeSpan.FromSeconds(2));//这个异常主线程无法捕捉到,因为是在子线程抛出的异常。需要在子线程中加入try...catch捕获异常throw new Exception("Boom!");}static void FaultyThread(){try{Console.WriteLine("Starting a faulty thread...");Thread.Sleep(TimeSpan.FromSeconds(1));throw new Exception("Boom");}catch (Exception ex){Console.WriteLine($"Exception handled: {ex.Message}");}}}
}

C#多线程编程实战 (线程同步)

2.1 简介

竞争条件:多个线程同时使用共享对象。需要同步这些线程使得共享对象的操作能够以正确的顺序执行

线程同步问题:多线程的执行并没有正确的同步,当一个线程执行递增和递减操作时,其他线程需要依次等待

线程同步解决方案:

无须共享对象:大部分时候可以通过重新设计来移除共享对象,去掉复杂的同步构造,避免多线程使用单一对象

必须共享对象:只使用原子操作,一个操作只占用一个量子的时间,无须实现其他线程等待当前操作完成

内核模式:将等待的线程置于阻塞状态,消耗少量的CPU资源,但会引入至少一次上下文切换,适用于线程等待较长时间

用户模式:只是简单的等待,线程等待会浪费CPU时间但是可以节省上下文切换消耗的CPU时间,适用于线程等待较短时间

混合模式:先尝试用户模式,如果等待时间较长,则会切换到内核模式

2.2 执行基本的原子操作

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Console.WriteLine("Incorrect counter");//没有限定,会遇到竞争条件,得出的结果大部分不是正确的var c = new Counter();var t1 = new Thread(() => TestCounter(c));var t2 = new Thread(() => TestCounter(c));var t3 = new Thread(() => TestCounter(c));t1.Start();t2.Start();t3.Start();t1.Join();t2.Join();t3.Join();Console.WriteLine($"Total count:{c.Count}");Console.WriteLine("--------------------------");Console.WriteLine("Correct counter");//使用Interlocked类提供的原子操作方法,无需锁定任何对象可得出正确结果var c1 = new CounterNoLock();t1 = new Thread(() => TestCounter(c1));t2 = new Thread(() => TestCounter(c1));t3 = new Thread(() => TestCounter(c1));t1.Start();t2.Start();t3.Start();t1.Join();t2.Join();t3.Join();Console.WriteLine($"Total count:{c1.Count}");Console.ReadLine();}static void TestCounter(CounterBase c){for (int i = 0; i < 100000; i++){c.Increment();c.Decrement();}}class Counter : CounterBase{private int _count;public int Count { get { return _count; } }public override void Decrement(){_count--;}public override void Increment(){_count++;}}class CounterNoLock : CounterBase{private int _count;public int Count { get { return _count; } }public override void Decrement(){//Interlocked提供了Increment()、Decrement()和Add等基本数学操作的原子方法Interlocked.Decrement(ref _count);}public override void Increment(){Interlocked.Increment(ref _count);}}abstract class CounterBase{public abstract void Increment();public abstract void Decrement();}}
}

注释:Interlocked提供了Increment()、Decrement()和Add等基本数学操作的原子方法,不用锁也可以得出正确结果

2.3 使用Mutex类

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){const string MutexName = "CSharpThreadingCookbook";//Mutex是一种原始的同步方式,只对一个线程授予对共享资源的独占访问//定义一个指定名称的互斥量,设置initialOwner标志为falseusing (var m = new Mutex(false, MutexName)){//如果互斥量已经被创建,获取互斥量,否则就执行else语句if (!m.WaitOne(TimeSpan.FromSeconds(5), false)){Console.WriteLine("Second instance is running!");}else{Console.WriteLine("Running!");Console.ReadLine();m.ReleaseMutex();}}//如果再运行同样的程序,则会在5秒内尝试获取互斥量,如果第一个程序按下了任何键,第二个程序开始执行。//如果等待5秒钟,第二个程序将无法获取该互斥量
        }}
}

注释:互斥量是全局操作对象,必须正确关闭,最好用using

2.4 使用SemaphoreSlim类

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){//启动6个线程,启动的顺序不一样for (int i = 0; i <= 6; i++){string threadName = "Thread " + i;int secondsToWait = 2 + 2 * i;var t = new Thread(() => AccessDatabase(threadName, secondsToWait));t.Start();}Console.ReadLine();}//SemaphoreSlim的构造函数参数为允许的并发线程数目static SemaphoreSlim semaphore = new SemaphoreSlim(4);static void AccessDatabase(string name, int seconds){Console.WriteLine($"{name} waits to access a database");semaphore.Wait();Console.WriteLine($"{name} was granted an access to a database");Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine($"{name} is Completed");//调用Release方法说明线程已经完成,可以开启一个新的线程了
            semaphore.Release();}}
}

注释:这里使用了混合模式,允许我们在等待时间很短的情况下无需上下文切换。SemaphoreSlim并不使用Windows内核信号量,而且也不支持进程间同步

2.5 使用AutoResetEvent类

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Thread t = new Thread(() => Process(10));t.Start();Console.WriteLine("Waiting for another thread to complete work");//开启一个线程后workEvent等待,直到收到Set信号
            workEvent.WaitOne();Console.WriteLine("First operation is complete");Console.WriteLine("Performing an operation on a main thread");Thread.Sleep(TimeSpan.FromSeconds(5));mainEvent.Set();Console.WriteLine("Now running the second operation on a second thread");workEvent.WaitOne();Console.WriteLine("Second operation is complete");Console.ReadLine();}//初始状态为unsignaled,子线程向主线程发信号private static AutoResetEvent workEvent = new AutoResetEvent(false);//初始状态为unsignaled,主线程向子线程发信号private static AutoResetEvent mainEvent = new AutoResetEvent(false);static void Process(int seconds){Console.WriteLine("Starting a long running work...");Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine("Work is done!");workEvent.Set();//将事件设为终止状态允许一个或多个线程继续Console.WriteLine("Waiting for a main thread to complete its work");mainEvent.WaitOne();//阻止当前线程,直到mainEvent收到信号Console.WriteLine("Starting second operation...");Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine("Work is done");workEvent.Set();}}
}

注释:AutoResetEvent采用的是内核模式,所以等待时间不能太长

2.6 使用ManualResetEventSlim类

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Thread t1 = new Thread(() => TravelThroughGates("Thread 1", 5));Thread t2 = new Thread(() => TravelThroughGates("Thread 2", 6));Thread t3 = new Thread(() => TravelThroughGates("Thread 3", 12));t1.Start();t2.Start();t3.Start();Thread.Sleep(TimeSpan.FromSeconds(6));Console.WriteLine("The gates are now open");mainEvent.Set();//将时间设置为有信号,从而让一个或多个等待该事件的线程继续Thread.Sleep(TimeSpan.FromSeconds(2));mainEvent.Reset();//将事件设置为非终止,从而导致线程受阻Console.WriteLine("The gates have been closed!");Thread.Sleep(TimeSpan.FromSeconds(10));Console.WriteLine("The gates are now open for the second time");mainEvent.Set();Thread.Sleep(TimeSpan.FromSeconds(2));Console.WriteLine("The gates have been closed!");mainEvent.Reset();Console.ReadLine();}static ManualResetEventSlim mainEvent = new ManualResetEventSlim(false);static void TravelThroughGates(string threadName, int seconds){Console.WriteLine($"{threadName} falls to sleep");Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine($"{threadName} waits for the gates to open!");mainEvent.Wait();//阻止当前线程Console.WriteLine($"{threadName} enter the gates!");}}
}

注释:ManualResetEventSlim工作方式像人群通过的大门,一直保持大门敞开直到调用reset,set相当于打开大门,reset相当于关闭大门

2.7 使用CountDownEvent类

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Console.WriteLine("Starting two operations");Thread t1 = new Thread(() => PerformOperation("Operation 1 is completed", 4));Thread t2 = new Thread(() => PerformOperation("Operation 2 is completed", 8));t1.Start();t2.Start();//开启了两个线程,调用Wait方法阻止当前线程,知道所有线程都完成
            countdown.Wait();Console.WriteLine("Both operations have been completed");countdown.Dispose();Console.ReadLine();}//计数器初始化CountdownEvent实例,计数器表示:当计数器个数完成操作发出信号static CountdownEvent countdown = new CountdownEvent(2);static void PerformOperation(string message, int seconds){Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine(message);//向CountdownEvent注册信息,并减少当前计数器数值
            countdown.Signal();}}
}

注释:如果Signal方法没有达到指定的次数,那么countdown.wait()会一直等待,所以请确保所有线程完成后都要调用Signal方法

2.8 使用Barrier类

using System;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Thread t1 = new Thread(() => PlayMusic("the gutarist", "play an amazing solo", 5));Thread t2 = new Thread(() => PlayMusic("the signer", "sing his song", 2));t1.Start();t2.Start();Console.ReadLine();}//后面的Lamda表达式是回调函数。执行完SignalAndWait后执行static Barrier barrier = new Barrier(2, b=>Console.WriteLine($"End of phase {b.CurrentPhaseNumber + 1}"));static void PlayMusic(string name, string message, int seconds){for (int i = 0; i < 3; i++){Console.WriteLine("===========================");Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine($"{name} starts to {message}");Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine($"{name} finishes to {message}");//等所有调用线程都结束
                barrier.SignalAndWait();}}}
}

注释:

2.9 使用ReaderWriterlockSlim类

using System;
using System.Collections.Generic;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){new Thread(Read) { IsBackground = true }.Start();new Thread(Read) { IsBackground = true }.Start();new Thread(Read) { IsBackground = true }.Start();new Thread(() => Write("Thread 1")) { IsBackground = true }.Start();new Thread(() => Write("Thread 2")) { IsBackground = true }.Start();Thread.Sleep(TimeSpan.FromSeconds(30));Console.ReadLine();}//实现线程安全static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();static Dictionary<int, int> items = new Dictionary<int, int>();static void Read(){Console.WriteLine("Reading contents of a dictionary");while (true){try{//读锁
                    _rw.EnterReadLock();foreach (var key in items.Keys){Thread.Sleep(TimeSpan.FromSeconds(0.1));}}finally{//计数为0时退出读取模式
                    _rw.ExitReadLock();}}}static void Write(string threadName){while (true){try{int newKey = new Random().Next(250);_rw.EnterUpgradeableReadLock();if (!items.ContainsKey(newKey)){try{//写锁
                            _rw.EnterWriteLock();items[newKey] = 1;Console.WriteLine($"New key {newKey} is added to a dictionary by a {threadName}");}finally{//计数为0时退出写入模式
                            _rw.ExitWriteLock();}}Thread.Sleep(TimeSpan.FromSeconds(0.1));}finally{//计数为0时退出可升级模式
                    _rw.ExitUpgradeableReadLock();}}}}
}

注释:从集合读取数据时,根据当前数据决定是否获取一个写锁并修改该集合。获取写锁后集合会处于阻塞状态。

2.10 使用SpinWait类

using System;
using System.Collections.Generic;
using System.Threading;namespace MulityThreadNote
{class Program{static void Main(string[] args){Thread t1 = new Thread(UserModeWait);Thread t2 = new Thread(HybridSpinWait);Console.WriteLine("Running user mode waiting");t1.Start();Thread.Sleep(20);_isComplete = true;Thread.Sleep(TimeSpan.FromSeconds(1));_isComplete = false;Console.WriteLine("Running hybrid SpinWait construct waiting");t2.Start();Thread.Sleep(5);_isComplete = true;Console.ReadLine();}//volatile 一个字段可能会被多个线程同时修改,不会被编译器和处理器优化为只能被单个线程访问static volatile bool _isComplete = false;static void UserModeWait(){while (!_isComplete){Console.WriteLine(".");}Console.WriteLine();Console.WriteLine("Waiting is complete");}static void HybridSpinWait(){var w = new SpinWait();while (!_isComplete){//执行单一自旋
                w.SpinOnce();//NextSpinWillYield:获取对SpinOnce的下一次调用是否将产生处理,同时触发强制上下文切换//显示线程是否切换为阻塞状态
                Console.WriteLine(w.NextSpinWillYield);}Console.WriteLine("Waiting is complete");}}
}

转载于:https://www.cnblogs.com/weihengblogs/p/8671290.html

C#多线程编程实战(二)相关推荐

  1. 深入浅出多线程编程实战(五)ThreadLocal详解(介绍、使用、原理、应用场景)

    深入浅出多线程编程实战(五)ThreadLocal详解(介绍.使用.原理.应用场景) 文章目录 一.ThreadLocal简介 二.ThreadLocal与Synchronized区别 三.Threa ...

  2. 《C#多线程编程实战(原书第2版)》——第3章 使用线程池 3.1 简介

    本节书摘来自华章出版社<C#多线程编程实战(原书第2版)>一书中的第3章,第3.1节,作者(美)易格恩·阿格佛温(Eugene Agafonov),黄博文 黄辉兰 译,更多章节内容可以访问 ...

  3. Java多线程编程实战指南

    内容简介 随着CPU 多核时代的到来,多线程编程在充分利用计算资源.提高软件服务质量方面扮演了越来越重要的角色.而解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案.然而,多线程编 ...

  4. Java多线程编程实战指南+设计模式篇pdf

    下载地址:网盘下载 随着CPU 多核时代的到来,多线程编程在充分利用计算资源.提高软件服务质量方面扮演了越来越重要的角色.而 解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案.然 ...

  5. java多线程编程_Java多线程编程实战指南+设计模式篇.pdf

    Java多线程编程实战指南+设计模式篇.pdf 对Java架构技术感兴趣的工程师朋友们可以关注我,转发此文后私信我"Java"获取更多Java编程PDF资料(附送视频精讲) 关注我 ...

  6. 《C#多线程编程实战(原书第2版)》——3.2 在线程池中调用委托

    本节书摘来自华章出版社<C#多线程编程实战(原书第2版)>一书中的第3章,第3.2节,作者(美)易格恩·阿格佛温(Eugene Agafonov),黄博文 黄辉兰 译,更多章节内容可以访问 ...

  7. 《C#多线程编程实战(原书第2版)》——3.6 在线程池中使用等待事件处理器及超时...

    本节书摘来自华章出版社<C#多线程编程实战(原书第2版)>一书中的第3章,第3.6节,作者(美)易格恩·阿格佛温(Eugene Agafonov),黄博文 黄辉兰 译,更多章节内容可以访问 ...

  8. 《C#多线程编程实战》读书笔记

    本文是一篇读书笔记,由<C#多线程编程实战>一书中的内容整理而来,主要梳理了.NET中多线程编程相关的知识脉络,从Thread.ThreadPool.Task.async/await.并发 ...

  9. java多线程实战指南_学习笔记《Java多线程编程实战指南》二

    2.1线程属性 属性 属性类型及用途 只读属性 注意事项 编号(id) long型,标识不同线程 是 不适合用作唯一标识 名称(name) String型,区分不同线程 否 设置名称有助于代码调试和问 ...

最新文章

  1. MyCAT-1.4-RC性能测试(初步施工)
  2. JAVA常用工具类→Date、SimpleDateFormat、Calendar、System、Math
  3. Py之Scipy:Scipy库(高级科学计算库)的简介、安装、使用方法之详细攻略
  4. unity 给图片边缘_Unity 截图选择框,中间全透明,边缘半透明
  5. Mac 安装的虚拟机win10系统,设置拥有各自的桌面
  6. Linux下 eclipse下载
  7. Mysql 统计按学生姓名分组,总成绩前十
  8. 开源问答系统开源软件
  9. VMWare 虚拟机网络共享给宿主机
  10. 智能微型断路器集计量,保护,控制于一体让用电更安全更智能-安科瑞 汤婉茹/孟强荣
  11. 硬盘连接计算机后怎么使用,台式机硬盘如何接笔记本【方法步骤】
  12. localhost 拒绝了我们的连接请求。Swagger
  13. win10搜索框突然不能使用了
  14. 互联网摸鱼日报(2023-03-01)
  15. python爬取晋江文学城_「天鹰文学网」著名小说网站《天鹰文学》宣布关站 - seo实验室...
  16. smbms超市管理系统项目总结
  17. 广东大学生计算机设计大赛,我校学子在广东省大学生计算机设计大赛中荣获一等奖...
  18. 彼得·蒂尔旗下大数据公司Palantir再融资2000万美元
  19. 个人版独立在线客服系统搭建教程_私有化开发安全有保障
  20. 希尔伯特曲线 java_《算法心得:高效算法的奥秘》PDF 下载

热门文章

  1. video safari不支持吗_您支持吗? 公园遛狗纳入 “不文明行为黑名单”
  2. 注册规划师 计算机科学与技术,关于公布2018年度注册城乡规划师考试合格人员名单的通知...
  3. C Hello World程序
  4. sql limit 子句_SQL按子句排序
  5. jms 教程_JMS教程–什么是JMS
  6. c语言如何将8个字符串串联_C ++中的字符串串联:串联字符串的4种方法
  7. [已解决]Hibernate程序未终止
  8. mvc crud_Spring MVC Hibernate MySQL集成CRUD示例教程
  9. 开课吧课堂:Java的内置异常汇总列表!
  10. UI设计入门:解析设计标注规范和图标规范