什么是线程互斥?
线程互斥(Thread Mutua lExclusion)是一种同步机制,用于确保在多线程环境中,同一时间只有一个线程可以访问特定的资源或代码段。线程互斥的主要目的是防止多个线程同时修改共享数据,从而避免数据不一致和竞态条件(Race Conditions)。
什么是竞态条件(Race Conditions)?
竞态条件是指程序的输出或行为取决于多个线程执行顺序的情况。当多个线程同时访问和修改共享资源时,如果没有适当的同步机制,可能会导致不可预测的结果。
线程互斥的主要机制:
在 C# 中,有多种机制可以实现线程互斥,以下是常见的几种:
-
Mutex(互斥体):Mutex 是一种同步基元,可以用于线程间的互斥访问。Mutex 可以是局部的(仅在当前进程内有效)或全局的(跨多个进程有效)。
using System; using System.Threading; public class MutexExample { private static Mutex _mutex; public static void Main() { _mutex = new Mutex(); // 创建两个线程 Thread thread1 = new Thread(DoWork); Thread thread2 = new Thread(DoWork); thread1.Start(); thread2.Start(); // 等待线程完成 thread1.Join(); thread2.Join(); _mutex.Close(); } public static void DoWork() { Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 尝试获取 Mutex"); _mutex.WaitOne(); // 获取 Mutex Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 获取到 Mutex"); // 执行临界区代码 Thread.Sleep(1000); Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 释放 Mutex"); _mutex.ReleaseMutex(); // 释放 Mutex } }
-
Semaphore(信号量):Semaphore 是一种同步基元,允许指定数量的线程同时访问共享资源。信号量可以是局部的或全局的。
using System; using System.Threading; public class SemaphoreExample { private static Semaphore _semaphore; public static void Main() { _semaphore = new Semaphore(2, 2); // 允许 2 个线程同时访问 // 创建多个线程 for (int i = 0; i < 5; i++) { Thread thread = new Thread(DoWork); thread.Start(i); } // 等待所有线程完成 Thread.Sleep(5000); } public static void DoWork(object state) { int id = (int)state; Console.WriteLine($"线程 {id} 尝试获取 Semaphore"); _semaphore.WaitOne(); // 获取 Semaphore Console.WriteLine($"线程 {id} 获取到 Semaphore"); // 执行临界区代码 Thread.Sleep(1000); Console.WriteLine($"线程 {id} 释放 Semaphore"); _semaphore.Release(); // 释放 Semaphore } }
-
SemaphoreSlim(轻量级信号量):适用于在单个应用程序或进程内使用。
using System; using System.Threading; public class SemaphoreSlimExample { private static SemaphoreSlim _semaphoreSlim; public static void Main() { _semaphoreSlim = new SemaphoreSlim(2, 2); // 允许 2 个线程同时访问 // 创建多个线程 for (int i = 0; i < 5; i++) { Thread thread = new Thread(DoWork); thread.Start(i); } // 等待所有线程完成 Thread.Sleep(5000); } public static void DoWork(object state) { int id = (int)state; Console.WriteLine($"线程 {id} 尝试获取 SemaphoreSlim"); _semaphoreSlim.Wait(); // 获取 SemaphoreSlim Console.WriteLine($"线程 {id} 获取到 SemaphoreSlim"); // 执行临界区代码 Thread.Sleep(1000); Console.WriteLine($"线程 {id} 释放 SemaphoreSlim"); _semaphoreSlim.Release(); // 释放 SemaphoreSlim } }
-
Lock 语句:适用于单个应用程序或进程内使用。
using System; using System.Threading; public class LockExample { private static readonly object _lock = new object(); public static void Main() { // 创建多个线程 for (int i = 0; i < 5; i++) { Thread thread = new Thread(DoWork); thread.Start(i); } // 等待所有线程完成 Thread.Sleep(5000); } public static void DoWork(object state) { int id = (int)state; Console.WriteLine($"线程 {id} 尝试获取 Lock"); lock (_lock) // 获取 Lock { Console.WriteLine($"线程 {id} 获取到 Lock"); // 执行临界区代码 Thread.Sleep(1000); Console.WriteLine($"线程 {id} 释放 Lock"); } } }
-
Monitor 类:提供了更高级的同步机制,可以实现 lock 语句的功能,并且提供了更多的控制选项。
using System; using System.Threading; public class MonitorExample { private static readonly object _lock = new object(); public static void Main() { // 创建多个线程 for (int i = 0; i < 5; i++) { Thread thread = new Thread(DoWork); thread.Start(i); } // 等待所有线程完成 Thread.Sleep(5000); } public static void DoWork(object state) { int id = (int)state; Console.WriteLine($"线程 {id} 尝试获取 Monitor"); Monitor.Enter(_lock); // 获取 Monitor try { Console.WriteLine($"线程 {id} 获取到 Monitor"); // 执行临界区代码 Thread.Sleep(1000); Console.WriteLine($"线程 {id} 释放 Monitor"); } finally { Monitor.Exit(_lock); // 释放 Monitor } } }
-
ReaderWriterLockSlim 类:允许多个线程同时进行读操作,但只允许一个线程进行写操作。适用于读操作频繁而写操作较少的场景。
using System; using System.Threading; public class ReaderWriterLockSlimExample { private static readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); private static int _sharedResource = 0; public static void Main() { // 创建多个读线程 for (int i = 0; i < 5; i++) { Thread thread = new Thread(ReadResource); thread.Start(i); } // 创建多个写线程 for (int i = 0; i < 5; i++) { Thread thread = new Thread(WriteResource); thread.Start(i); } // 等待所有线程完成 Thread.Sleep(5000); } public static void ReadResource(object state) { int id = (int)state; Console.WriteLine($"读线程 {id} 尝试获取读锁"); _rwLock.EnterReadLock(); try { Console.WriteLine($"读线程 {id} 获取到读锁"); Console.WriteLine($"读线程 {id} 读取资源值: {_sharedResource}"); Thread.Sleep(500); Console.WriteLine($"读线程 {id} 释放读锁"); } finally { _rwLock.ExitReadLock(); } } public static void WriteResource(object state) { int id = (int)state; Console.WriteLine($"写线程 {id} 尝试获取写锁"); _rwLock.EnterWriteLock(); try { Console.WriteLine($"写线程 {id} 获取到写锁"); _sharedResource++; Console.WriteLine($"写线程 {id} 写入资源值: {_sharedResource}"); Thread.Sleep(1000); Console.WriteLine($"写线程 {id} 释放写锁"); } finally { _rwLock.ExitWriteLock(); } } }