1:Mutex类可以同步两个单独的程序,只对一个线程授予对共享资源的独占访问。具名的互斥量是全局的操作系统对象,请务必正确关闭互斥量,最好使用using代码块来包裹互斥量对象。
示例代码:
static void Main(string[] args)
{
const string MutexName = "CSharpThreadingCookbook";
{
const string MutexName = "CSharpThreadingCookbook";
using (var m = new Mutex(false, MutexName))
{
{
//设置5秒钟内获取该互斥量
if (!m.WaitOne(TimeSpan.FromSeconds(5), false))
{
WriteLine("Second instance is running!");
}
else
{
WriteLine("Running!");
ReadLine();
m.ReleaseMutex();//释放互斥量
}
}
}
if (!m.WaitOne(TimeSpan.FromSeconds(5), false))
{
WriteLine("Second instance is running!");
}
else
{
WriteLine("Running!");
ReadLine();
m.ReleaseMutex();//释放互斥量
}
}
}
2:SemaphoreSlim类限制了访问同一资源的线程数量。Semaphore类是SemaphoreSlim类的一个老版本,Semaphore类可以像Mutex一样具名,并且可以在不同的程序中同步线程,但是SemaphoreSlim并不使用Windows内核信号量,而且也不支持进程间同步,所以在跨程序同步的场景下可以使用Semaphore。
3:CountDownEvent类可以等待一定数量的线程完成操作。
示例代码:
static void Main(string[] args)
{
WriteLine("Starting two operations");
var t1 = new Thread(() => PerformOperation("Operation 1 is completed", 4));
var t2 = new Thread(() => PerformOperation("Operation 2 is completed", 8));
t1.Start();
t2.Start();
_countdown.Wait();//如果_countdown.Signal()没达到指定次数,_countdown.Wait()将一直等待
WriteLine("Both operations have been completed.");
_countdown.Dispose();
}
{
WriteLine("Starting two operations");
var t1 = new Thread(() => PerformOperation("Operation 1 is completed", 4));
var t2 = new Thread(() => PerformOperation("Operation 2 is completed", 8));
t1.Start();
t2.Start();
_countdown.Wait();//如果_countdown.Signal()没达到指定次数,_countdown.Wait()将一直等待
WriteLine("Both operations have been completed.");
_countdown.Dispose();
}
static CountdownEvent _countdown = new CountdownEvent(2);
static void PerformOperation(string message, int seconds)
{
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine(message);
_countdown.Signal();//注册信号,减少计数
}
{
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine(message);
_countdown.Signal();//注册信号,减少计数
}
4:Barrier类用于组织多个线程及时在某个时刻碰面,其提供了一个回调函数,每次线程调用了SignalAndWait方法后该回调函数会被执行。适用于多任务多阶段同步执行的场景。
static void Main(string[] args)
{
var t1 = new Thread(() => PlayMusic("任务1"));
var t2 = new Thread(() => PlayMusic("任务2"));
t1.Start();
t2.Start();
ReadLine();
}
//当有两个线程都执行完后就会执行回调。
static Barrier _barrier = new Barrier(2,b => {
WriteLine($"两个任务全部完成阶段: {b.CurrentPhaseNumber + 1}");
});
{
var t1 = new Thread(() => PlayMusic("任务1"));
var t2 = new Thread(() => PlayMusic("任务2"));
t1.Start();
t2.Start();
ReadLine();
}
//当有两个线程都执行完后就会执行回调。
static Barrier _barrier = new Barrier(2,b => {
WriteLine($"两个任务全部完成阶段: {b.CurrentPhaseNumber + 1}");
});
static void PlayMusic(string name)
{
for (int i = 1; i < 3; i++)
{
WriteLine($"{name} 执行阶段{i}");
_barrier.SignalAndWait();
}
}
{
for (int i = 1; i < 3; i++)
{
WriteLine($"{name} 执行阶段{i}");
_barrier.SignalAndWait();
}
}
5:ReaderWriterLockSlim类允许多个线程同时读取与独占写。ReaderWriterLockSlim提供了四个方法来得到和释放读写锁:EnterReadLock(),ExitReadLock(),EnterWriteLock(),ExitWriteLock()。另外还提供了两个方法EnterUpgradeableReadLock()和ExitUpgradeableReadLock()两个方法来实现读写时的线程安全。通常情况下,当对全局list进行读写时的步骤为:
1 获得一个读取锁
2 测试list是否包含item,如果是,则返回
3 释放读取锁
4 获得一个写入锁
5 写入item到list中,释放写入锁。
但 是在步骤3、4之间,当另外一个线程可能偷偷修改List(比如说添加同样一个Item),ReaderWriterLockSlim通过提供第三种锁来 解决这个问题,这就是UpgradeableReadLock。一个可升级锁和ReadLock类似,只是它能够通过一个原子操作,被提升为WriteLock。使用方法如下:
1 调用EnterUpgradeableReadLock(进入可升级模式,可自动将读锁升级为写锁,无需切换)
2 读操作(测试list是否包含item,如果是,则调用ExitUpgradeableReadLock)
3 调用 EnterWriteLock
4 写操作(写入item到list中)
5 调用ExitWriteLock
6 其他读取的过程
7 调用ExitUpgradeableReadLock
可升级模式适用于线程通常读取受保护资源的内容,但在某些条件满足时可能需要写入的情况。使用可升级锁可以方便的从读锁中升级为写锁,而不需要进行切换,以增加损耗。