除了lock和Semaphore之外,C# 还有其他的线程同步方法,如 Monitor, Mutex, ReaderWriterLockSlim 和 ManualResetEvent等。
目录在常见的编程语言中,同步原语可以分类为哪些?
锁Locks
信号量Semaphores
互斥量Mutexes
条件变量Condition Variables
屏障Barriers
这些同步原语在不同编程语言和操作系统中有不同的实现方式和命名,但它们的基本概念和功能是相似的。
关于锁的名词解释
放弃名词解释——看这篇文章了解锁的分类 https://juejin.cn/post/7010305230256488485
锁可重入(Reentrant)是指一个线程可以多次获得同一个锁,而不会产生死锁。当一个线程已经拥有了一个锁,再次尝试获得相同的锁时,该线程可以继续执行,而不会阻塞等待锁释放。
自旋锁(SpinLock)是一种特殊类型的锁,当一个线程试图获得已被其他线程持有的锁时,不会立即进入阻塞状态。相反,该线程会在一个循环中忙等待(busy-waiting),不断地检查锁是否可用。自旋锁是一种低级别的同步原语,通常在需要极低延迟和高性能的场景下使用。
C#里有哪些线程同步的办法
lock
优点:简单易用,适用于大多数场景,可重入。
缺点:无法手动释放锁,无法跨进程使用。
using System;
using System.Threading;
class Example
{
private static object syncObj = new object();
private static int counter = 0;
static void Main()
{
Thread t1 = new Thread(IncrementCounter);
Thread t2 = new Thread(IncrementCounter);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("Counter: " + counter);
}
static void IncrementCounter()
{
for (int i = 0; i < 1000; i++)
{
lock (syncObj)
{
counter++;
}
}
}
}
Semaphore
优点:可以控制同时访问资源的线程数量,可跨进程使用。
缺点:相对于lock,使用起来稍复杂。
using System;
using System.Threading;
class Example
{
private static Semaphore sem = new Semaphore(1, 1); // Initial count: 1, Maximum count: 1
private static int counter = 0;
static void Main()
{
Thread t1 = new Thread(IncrementCounter);
Thread t2 = new Thread(IncrementCounter);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("Counter: " + counter);
}
static void IncrementCounter()
{
for (int i = 0; i < 1000; i++)
{
sem.WaitOne();
counter++;
sem.Release();
}
}
}
Monitor
优点:灵活性高,可手动控制锁的获取和释放,可重入。
缺点:使用起来较复杂,容易出错,无法跨进程使用。
using System;
using System.Threading;
class Example
{
private static object syncObj = new object();
private static int counter = 0;
static void Main()
{
Thread t1 = new Thread(IncrementCounter);
Thread t2 = new Thread(IncrementCounter);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("Counter: " + counter);
}
static void IncrementCounter()
{
for (int i = 0; i < 1000; i++)
{
bool lockTaken = false;
try
{
Monitor.Enter(syncObj, ref lockTaken);
counter++;
}
finally
{
if (lockTaken)
{
Monitor.Exit(syncObj);
}
}
}
}
}
Mutex
优点:可跨进程使用,可重入。
缺点:性能相对较差,开销较大,相对于lock和Monitor,使用起来较复杂。
using System;
using System.Threading;
class Example
{
private static Mutex mutex = new Mutex();
private static int counter = 0;
static void Main()
{
Thread t1 = new Thread(IncrementCounter);
Thread t2 = new Thread(IncrementCounter);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("Counter: " + counter);
}
static void IncrementCounter()
{
for (int i = 0; i < 1000; i++)
{
mutex.WaitOne();
counter++;
mutex.ReleaseMutex();
}
}
}
ReaderWriterLockSlim
优点:读写锁定分离,适用于读操作远多于写操作的场景,性能较高。
缺点:使用起来相对复杂,不可跨进程使用。
using System;
using System.Threading;
class Example
{
private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
private static int counter = 0;
static void Main()
{
Thread t1 = new Thread(IncrementCounter);
Thread t2 = new Thread(IncrementCounter);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("Counter: " + counter);
}
static void IncrementCounter()
{
for (int i = 0; i < 1000; i++)
{
rwLock.EnterWriteLock();
counter++;
rwLock.ExitWriteLock();
}
}
}
ManualResetEvent
优点:灵活,可以用于多个线程之间的信号传递。
缺点:不是传统意义上的同步锁,只能控制线程的等待和继续,不适用于保护资源的互斥访问。
using System;
using System.Threading;
class Example
{
private static ManualResetEvent mre = new ManualResetEvent(false);
private static int counter = 0;
static void Main()
{
Thread t1 = new Thread(IncrementCounter);
Thread t2 = new Thread(IncrementCounter);
t1.Start();
t2.Start();
mre.Set();
t1.Join();
t2.Join();
Console.WriteLine("Counter: " + counter);
}
static void IncrementCounter()
{
mre.WaitOne();
for (int i = 0; i < 1000; i++)
{
counter++;
}
}
}
请注意,这些代码示例中的同步方法各有优缺点。在实际应用中,请根据具体需求选择合适的同步方法。
在实际应用中,需要根据需求和场景来选择合适的同步方法。
- 简单场景下,可以优先考虑使用lock。
- 如果需要跨进程同步,可以使用Mutex或Semaphore。
- 在读写操作不平衡的情况下,可以考虑使用ReaderWriterLockSlim。
- 对于线程之间的信号传递,可以使用ManualResetEvent。
- 而在需要更多控制和灵活性的场景下,可以考虑使用Monitor。