📅  最后修改于: 2023-12-03 15:00:17.553000             🧑  作者: Mango
在多线程编程中,为了避免线程间的竞争条件(Race Condition)和死锁(Deadlock),需要使用线程同步的技术。C#提供了多种线程同步的方式,包括锁(lock)、互斥量(Mutex)、事件(Event)、信号量(Semaphore)等。
锁是最常见的线程同步方式。C#中可以通过lock关键字来实现锁。锁保证在同一时间只有一个线程可以访问指定代码块。
class Program
{
static object locker = new object();
static void Main(string[] args)
{
Thread t1 = new Thread(DoWork);
Thread t2 = new Thread(DoWork);
t1.Start();
t2.Start();
Console.ReadLine();
}
static void DoWork()
{
lock (locker)
{
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " start.");
Thread.Sleep(1000);
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " end.");
}
}
}
在上面的例子中,我们使用lock关键字来实现对 DoWork
方法的同步。当一个线程进入该方法时,它会获取锁,执行指定代码块,然后释放锁。其他线程进入该方法时,必须等待前一个线程释放锁后才可以执行。
互斥量也可以用来实现线程同步。和锁不同的是,互斥量可以被跨进程地共享访问。
class Program
{
static Mutex mutex = new Mutex();
static void Main(string[] args)
{
Thread t1 = new Thread(DoWork);
Thread t2 = new Thread(DoWork);
t1.Start();
t2.Start();
Console.ReadLine();
}
static void DoWork()
{
mutex.WaitOne();
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " start.");
Thread.Sleep(1000);
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " end.");
mutex.ReleaseMutex();
}
}
在上面的例子中,我们使用Mutex类来实现对 DoWork
方法的同步。一个线程在获取互斥量后才可执行指定代码块,执行完后又释放互斥量,其他线程才能继续执行。
事件也是一种常见的线程同步方式。和互斥量不同的是,事件可以在多个线程之间传递信号,用于同步事件的发生和处理。
class Program
{
static AutoResetEvent autoEvent = new AutoResetEvent(false);
static void Main(string[] args)
{
Thread t1 = new Thread(() =>
{
Thread.Sleep(2000);
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " starts signal.");
autoEvent.Set();
});
Thread t2 = new Thread(DoWork);
t1.Start();
t2.Start();
Console.ReadLine();
}
static void DoWork()
{
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " waiting signal.");
autoEvent.WaitOne();
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " received signal and start.");
Thread.Sleep(1000);
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " end.");
}
}
在上面的例子中,我们使用AutoResetEvent自动重置事件类来实现对 DoWork
方法的同步。一个线程在调用 WaitOne
方法时等待事件的发生,当事件被触发时调用 Set
方法来通知其他线程,然后自动重置事件。其他线程在调用 WaitOne
方法时等待事件的发生,当事件被触发时就可以执行指定代码块。
信号量用于控制同时执行的线程数量。通过控制信号量的初始值和每次释放的数量,我们可以控制允许执行的线程数量。
class Program
{
static Semaphore sem = new Semaphore(2, 2);
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
Thread t = new Thread(DoWork);
t.Start();
}
Console.ReadLine();
}
static void DoWork()
{
sem.WaitOne();
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " start.");
Thread.Sleep(1000);
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " end.");
sem.Release();
}
}
在上面的例子中,我们使用Semaphore类来实现对 DoWork
方法的同步。初始值和每次释放的数量都设为2,因此最多只能有两个线程同时执行指定代码块。当一个线程获取到信号量后,执行指定代码块,然后释放信号量让其他线程获取。