C#多线程编程实战(二)
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();
Console.ReadLine();
} static void PrintNumbers()
{
Console.WriteLine("Starting...");
for (int i = ; i < ; i++)
{
Console.WriteLine(i);
}
} //注意:要使用ParameterizedThreadStart,定义的参数必须为object
static void PrintNumbers(object count)
{
Console.WriteLine("Starting...");
for (int i = ; 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 = ; i < ; i++)
{ Console.WriteLine(i);
}
} static void PrintNumbersWithDelay()
{
Console.WriteLine("Starting...");
for (int i = ; i < ; i++)
{
Thread.Sleep(TimeSpan.FromSeconds());
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 = ; i < ; i++)
{ Console.WriteLine(i);
}
} static void PrintNumbersWithDelay()
{
Console.WriteLine("Starting...");
for (int i = ; i < ; i++)
{
Thread.Sleep(TimeSpan.FromSeconds());
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());
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 = ; i < ; i++)
{ Console.WriteLine(i);
}
} static void PrintNumbersWithDelay()
{
Console.WriteLine("Starting...");
for (int i = ; i < ; i++)
{
Thread.Sleep(TimeSpan.FromSeconds());
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 = ; i < ; i++)
{
Console.WriteLine(t1.ThreadState.ToString());
}
Thread.Sleep(TimeSpan.FromSeconds());
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 = ; i < ; i++)
{
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine(i);
}
} private static void DoNothing()
{
Thread.Sleep(TimeSpan.FromSeconds());
}
}
}
注释:使用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());
Console.WriteLine("Running on a single Core");
//让操作系统的所有线程运行在单个CPU核心上
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr();
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());
sample.Stop();
}
} class ThreadSample
{
private bool _isStopped = false;
public void Stop()
{
_isStopped = true;
} public void CountNumbers()
{
long counter = ;
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();
var sampleBackground = new ThreadSample();
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 = ; 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(); 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();
t2.Join();
Console.WriteLine("--------------------------"); //使用lambda表达式引用另一个C#对方的方式被称为闭包。当在lambda表达式中使用任何局部变量时,C#会生成一个类,并将该变量作为该类的一个属性,但是我们无须定义该类,C#编译器会自动帮我们实现
Thread t3 = new Thread(()=> CountNumbers());
t3.Name = "ThreadThree";
t3.Start();
t3.Join();
Console.WriteLine("--------------------------"); int i = ;
Thread t4 = new Thread(() => PrintNumber(i)); i = ;
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 = ; 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 = ; 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 = ; i < ; 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 = ; 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();
Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
//直接使用Monitor.TryEnter, 如果在第二个参数之前还未获取到lock保护的资源会返回false
if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds()))
{
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();
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());
//这个异常主线程无法捕捉到,因为是在子线程抛出的异常。需要在子线程中加入try...catch捕获异常
throw new Exception("Boom!");
}
static void FaultyThread()
{
try
{
Console.WriteLine("Starting a faulty thread...");
Thread.Sleep(TimeSpan.FromSeconds());
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 = ; i < ; 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标志为false
using (var m = new Mutex(false, MutexName))
{
//如果互斥量已经被创建,获取互斥量,否则就执行else语句
if (!m.WaitOne(TimeSpan.FromSeconds(), 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 = ; i <= ; i++)
{
string threadName = "Thread " + i;
int secondsToWait = + * i;
var t = new Thread(() => AccessDatabase(threadName, secondsToWait));
t.Start();
} Console.ReadLine();
}
//SemaphoreSlim的构造函数参数为允许的并发线程数目
static SemaphoreSlim semaphore = new SemaphoreSlim(); 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());
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());
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", ));
Thread t2 = new Thread(() => TravelThroughGates("Thread 2", ));
Thread t3 = new Thread(() => TravelThroughGates("Thread 3", ));
t1.Start();
t2.Start();
t3.Start();
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("The gates are now open");
mainEvent.Set();//将时间设置为有信号,从而让一个或多个等待该事件的线程继续
Thread.Sleep(TimeSpan.FromSeconds());
mainEvent.Reset();//将事件设置为非终止,从而导致线程受阻
Console.WriteLine("The gates have been closed!");
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("The gates are now open for the second time");
mainEvent.Set();
Thread.Sleep(TimeSpan.FromSeconds());
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", ));
Thread t2 = new Thread(() => PerformOperation("Operation 2 is completed", ));
t1.Start();
t2.Start();
//开启了两个线程,调用Wait方法阻止当前线程,知道所有线程都完成
countdown.Wait();
Console.WriteLine("Both operations have been completed");
countdown.Dispose();
Console.ReadLine();
}
//计数器初始化CountdownEvent实例,计数器表示:当计数器个数完成操作发出信号
static CountdownEvent countdown = new CountdownEvent(); 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", ));
Thread t2 = new Thread(() => PlayMusic("the signer", "sing his song", ));
t1.Start();
t2.Start();
Console.ReadLine();
}
//后面的Lamda表达式是回调函数。执行完SignalAndWait后执行
static Barrier barrier = new Barrier(, b=>Console.WriteLine($"End of phase {b.CurrentPhaseNumber + 1}")); static void PlayMusic(string name, string message, int seconds)
{
for (int i = ; i < ; 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());
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();
_rw.EnterUpgradeableReadLock();
if (!items.ContainsKey(newKey))
{
try
{
//写锁
_rw.EnterWriteLock();
items[newKey] = ;
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();
_isComplete = true;
Thread.Sleep(TimeSpan.FromSeconds());
_isComplete = false;
Console.WriteLine("Running hybrid SpinWait construct waiting");
t2.Start();
Thread.Sleep();
_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");
}
}
}
C#多线程编程实战(二)的更多相关文章
- Java多线程编程实战指南(核心篇)读书笔记(二)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(五)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(四)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76690961冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(三)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76686044冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(一)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76422930冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- 《Java多线程编程实战指南(核心篇)》阅读笔记
<Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...
- Java多线程编程实战读书笔记(一)
多线程的基础概念本人在学习多线程的时候发现一本书——java多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.
- ASP.Net教程系列:多线程编程实战(一)
Web开发中使用多线程可以增强用户体验,尤其是多用户.多任务.海量数据和资源紧张的情况下.所以我们的ASP.Net教程设立多线程编程实战专题.下面这些代码范例都是入门级的,希望对对大家学习ASP.Ne ...
- Java多线程编程实战02:多线程编程模型
多线程编程模型 线程安全名词 串行.并发和并行 串行:一个人,将任务一个一个完成 并发:一个人,有策略地同时做多件事情 并行:多个人,每人做一个事情 竞态 名词 竞态:计算结果的正确性与时间有关的现象 ...
随机推荐
- vue.js 入门学习
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- string标准C++中的的用法总结(转)
转自:http://www.cnblogs.com/xFreedom/archive/2011/05/16/2048037.html 相信使用过MFC编程的朋友对CString这个类的印象应该非常深刻 ...
- laravel Blade 模板引擎
与视图文件紧密关联的就是模板代码,我们在视图文件中通过模板代码和 HTML 代码结合实现视图的渲染.和很多其他后端语言不同,PHP 本身就可以当做模板语言来使用,但是这种方式有很多缺点,比如安全上的隐 ...
- Win#password;;processon #clone;;disassemble;;source find
1.密码学思维导图 源地址:https://www.processon.com/view/5a61d825e4b0c090524f5b8b 在这之前给大家分享 如何在 processon上搜索公开克隆 ...
- java实现 排序算法(鸡尾酒排序&选择排序&插入排序&二分插入排序)
1.鸡尾酒排序算法 源程序代码: package com.SuanFa; public class Cocktial { public static void main(String[] arg ...
- Imperial roads 非严格次小生成树
cf测评姬比uva快了五倍... /* 不管这条边是不是在mst上,直接跑lca求出路径上的最大边w即可 ans=mst-w+dist(u,v) */ #include<bits/stdc++. ...
- Nginx详解三:Nginx基础篇之yum安装
Nginx快速搭建 Mainline version ----开发版:具有最新功能的版本,用于测试.研究.学习,不用于企业生成环境 Stable version----稳定版:官方认可,且通过测试的 ...
- Allegro PCB Design GXL (legacy) 设置十字大光标
Allegro PCB Design GXL (legacy) version 16.6-2015 1.菜单:Setup > User Preferences... 2.User Prefere ...
- python http请求类
# -*- coding: UTF-8 -*- # coding="utf-8" import httplib2 import json from urllib.parse imp ...
- NPOI操作Excel(三)--解析Excel
通过前面两篇的基础学习,我们对NPOI有了一定了了解,下面就开始进入实战,解析下面格式的Excel(下面只是列举了几个例子),并保存入库 首先我们先分析一下,要解析这样的Excel,需要把指标[橘色背 ...