概念简介
线程同步
- 当一个线程执行递增和递减操作时,其他线程需要依次次等待,这种问题通常被称为线程同步。
###原子操作
- 一个操作只占用一个量子的时间,一次就可以完成。
###阻塞状态(内核模式)
讲等待的线程置于阻塞状态,当线程处于阻塞状态时,只会占用尽量少的CPU时间。然而这样就会引入至少一次所谓的上下文切换。上下文切换是指操作系统的线程调度器。该调度器会保存等待的线程的状态,并切换另一个线程,一次恢复等待的线程状态。这需要消耗相当多的资源。然而如果线程要被挂起很长时间,那么这么做是值得的。这种方式又被称为内核模式。因为只有操作系统的内核才能阻止线程使用CPU时间。
用户模式
万一线程只需要等待一小段时间,最好只是简单的等待,而不用将线程切换到阻塞状态。虽然线程等待依然会浪费CPU时间。但我们节省了上下文切换耗费的CPU时间。该方式被称为用户模式,该方式非常轻量,速度很快,但如果线程需要等待较长时间则会浪费大量的CPU时间。
###混合模式
- 混合模式是内核模式和用户模式的混合,混合模式先尝试使用用户模式等待,如果线程等待了足够长的时间,则切换到阻塞状态(即内核模式)以节省CPU资源
原子操作
借助于使用Interlocked类,该类提供了Increment、decrement和add等基本数学操作的原子方法,以至于帮助我们在编程时无需使用锁
Intellocked类 参见msdn https://msdn.microsoft.com/zh-cn/library/system.threading.interlocked.aspx
事例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace testThread_atom
{
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 Counter:{0}",c.counter);
Console.WriteLine("--------------------------");
Console.WriteLine("Correct counter");
var c1 = new CounterWithNolock();
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 Counter:{0}",c1.Counter);
}
static void testcounter(CounterBase c)
{
for (int i = 0; i < 10000; i++)
{
c.Increment();
c.Decrement();
}
}
}
abstract class CounterBase
{
public abstract void Increment();
public abstract void Decrement();
}
class Counter : CounterBase
{
private int _counter;
public int counter { get { return _counter; } }
public override void Increment()
{
_counter++;
}
public override void Decrement()
{
_counter--;
}
}
class CounterWithNolock : CounterBase
{
private int _counter;
public int Counter { get { return _counter; } }
public override void Increment()
{
Interlocked.Increment(ref _counter);
}
public override void Decrement()
{
Interlocked.Decrement(ref _counter);
}
}
}
- 结果
使用Mutex类
Mutex是一种原始的同步方式。其只是一个对线程授予对共享资源的独自访问。
实例
static void Main(string[] args)
{
const string MutexName = "CsharpThreadingCookBook";
using(var m=new Mutex(false,MutexName))
{
if (!m.WaitOne(TimeSpan.FromSeconds(5), false))
{
Console.WriteLine("Second instance is running");
Console.ReadLine();
}
else
{
Console.WriteLine("Running!");
Console.ReadLine();
m.ReleaseMutex();
}
}
}
结果
当程序启动时,定义了一个指定名称的互斥量,设置initalOwner标志为false。这意味着如果互斥量已经被创建,则允许程序获取该互斥量。如果没有获取互斥量,程序则简单的显示running,等待直到按下了任何键,然后释放该互斥量并退出。
如果再运行同样一个程序,则会5S内尝试获取互斥量。如果此时在第一个程序中按下任何键,第二个程序则会开始执行。然而如果保持等待5S第二个程序将无法获取到改互斥量。第一个程序没有按键
第一个程序按键 第二个程序显示依然是running
SemaphoreSlim 类
对可同时访问资源或资源池的线程数加以限制的 Semaphore 的轻量替代
SemaphoreSlim msdn地址https://msdn.microsoft.com/zh-cn/library/system.threading.semaphoreslim.aspx
事例 这里使用了混合模式
class Program
{
static SemaphoreSlim _semphore = new SemaphoreSlim(2);
static void Main(string[] args)
{
for (int i = 1; i <= 6; i++)
{
string threadname = "Thread" + i;
int secondtowait = 2 + 2 * i;
var t = new Thread(()=>accessdatabase(threadname,secondtowait));
t.Start();
}
}
static void accessdatabase(string name,int seconds)
{
Console.WriteLine("{0} waits access a database",name);
_semphore.Wait();
Console.WriteLine("{0} was granted an access to a database",name);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("{0} is Completed",name);
_semphore.Release();
}
}
- 结果
使用AutoResetEvent类
借助于AutoResetEvent类从一个线程向另一个线程发送通知,AutoResetEvent 类可以通知等待的线程有某件事情发生(内核时间模式,等待时间不能太长)。
AutoResetEvent类 msdn地址https://msdn.microsoft.com/zh-cn/library/system.threading.autoresetevent.aspx
class Program
{
private static AutoResetEvent _workevent = new AutoResetEvent(false);
private static AutoResetEvent _mainevent = new AutoResetEvent(false);
static void Main(string[] args)
{
var t = new Thread(()=>process(10));
t.Start();
Console.WriteLine("waiting for another thread to complete work");
_workevent.WaitOne();
Console.WriteLine(" first operation is completed");
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 completed");
}
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();
Console.WriteLine("starting second operation");
Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("work is done");
_workevent.Set();
}
}
- 结果

使用ManualResetEventSlim类
借助于ManualResetEventSlim类 在线程间以更灵活的方式传递信号(混合模式)。
ManualResetEventSlim类 msdn地址https://msdn.microsoft.com/zh-cn/library/system.threading.manualreseteventslim(v=VS.100).aspx
class Program
{
static ManualResetEventSlim _mainevent = new ManualResetEventSlim(false);
static void Main(string[] args)
{
var t1 = new Thread(() => TravelThroughGates("Thread 1",5));
var t2 = new Thread(() => TravelThroughGates("Thread 2", 6));
var 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();
Console.WriteLine("the gates have been closed");
Thread.Sleep(TimeSpan.FromSeconds(10));
Console.WriteLine("the gates are now open for second time");
_mainevent.Set();
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("the gates have been closed");
_mainevent.Reset();
Console.ReadLine();
}
static void TravelThroughGates(string threadname,int seconds)
{
Console.WriteLine("{0} fails to sleep",threadname);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("{0} waits for the gates to open ",threadname);
_mainevent.Wait();
Console.WriteLine("{0} enters the gates",threadname);
}
}
- 结果