C#线程同步的三类情景分析
在多线程的应用中,为了确保数据的正确性,我们需要对线程进行同步,避免多个线程同时对同一份数据进行修改。C#中提供了多种同步机制,其中包括线程同步的三类情景:Mutex、Monitor和AutoResetEvent。
Mutex
Mutex是由操作系统提供的同步原语,可以保证只有一个线程访问临界区。典型的使用方式如下:
using System.Threading;
Mutex mutex = new Mutex();
void Method1()
{
mutex.WaitOne();
try
{
// 临界区代码
}
finally
{
mutex.ReleaseMutex();
}
}
void Method2()
{
mutex.WaitOne();
try
{
// 临界区代码
}
finally
{
mutex.ReleaseMutex();
}
}
这里使用Mutex来保护两个方法中的临界区,确保它们不会被同时访问。当一个线程获得了Mutex的锁时,其他线程将会阻塞,直到拥有锁的线程释放它。
Monitor
Monitor是.NET Framework提供的同步机制,它可以保证在同一时间内只有一个线程访问临界区。典型的使用方式如下:
using System.Threading;
object lockObject = new object();
void Method1()
{
lock(lockObject)
{
// 临界区代码
}
}
void Method2()
{
lock(lockObject)
{
// 临界区代码
}
}
这里使用lock关键字来保证两个方法中的临界区不会同时被访问。当一个线程进入临界区时,其他线程将会阻塞,直到当前线程离开临界区。
AutoResetEvent
AutoResetEvent是一个基于事件的同步机制,它允许一个线程等待另一个线程的信号。典型的使用方式如下:
using System.Threading;
AutoResetEvent autoEvent = new AutoResetEvent(false);
void Method1()
{
// 临界区代码
autoEvent.Set();
}
void Method2()
{
autoEvent.WaitOne();
// 临界区代码
}
这里使用AutoResetEvent来等待Method1的信号,当Method1完成临界区操作后,调用autoEvent.Set()来通知正在等待的线程。Method2调用autoEvent.WaitOne()来等待信号的到来,从而保证临界区操作的同步性。
示例说明
下面是两个示例,演示了如何使用Mutex和Monitor来保护共享资源。
Mutex示例
using System.Threading;
class Counter
{
private int count = 0;
private Mutex mutex = new Mutex();
public void Increment()
{
mutex.WaitOne();
try
{
count++;
}
finally
{
mutex.ReleaseMutex();
}
}
public void Decrement()
{
mutex.WaitOne();
try
{
count--;
}
finally
{
mutex.ReleaseMutex();
}
}
public int GetCount()
{
mutex.WaitOne();
try
{
return count;
}
finally
{
mutex.ReleaseMutex();
}
}
}
class Program
{
static void Main(string[] args)
{
Counter c = new Counter();
Thread t1 = new Thread(() => {
for(int i = 0; i < 100000; i++)
{
c.Increment();
}
});
Thread t2 = new Thread(() => {
for(int i = 0; i < 100000; i++)
{
c.Decrement();
}
});
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Count: {c.GetCount()}");
}
}
这个示例演示了如何使用Mutex来保护一个计数器的临界区。Counter类有三个方法:Increment、Decrement和GetCount,这些方法分别执行对计数器的加、减和读操作。每个方法都获取Mutex的锁,并在临界区执行相应的操作。需要注意的是,在GetCount方法中也需要获取Mutex的锁,以保证读操作的安全性。
Monitor示例
using System.Threading;
class BankAccount
{
private int balance = 0;
public void Deposit(int amount)
{
lock(this)
{
balance += amount;
}
}
public void Withdraw(int amount)
{
lock(this)
{
if(balance >= amount)
{
balance -= amount;
}
else
{
throw new Exception("Insufficient funds");
}
}
}
public int GetBalance()
{
lock(this)
{
return balance;
}
}
}
class Program
{
static void Main(string[] args)
{
BankAccount account = new BankAccount();
Thread t1 = new Thread(() => {
for(int i = 0; i < 100000; i++)
{
account.Deposit(10);
}
});
Thread t2 = new Thread(() => {
for(int i = 0; i < 100000; i++)
{
account.Withdraw(10);
}
});
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Balance: {account.GetBalance()}");
}
}
这个示例演示了如何使用Monitor来保护一个银行账户的临界区。BankAccount类有三个方法:Deposit、Withdraw和GetBalance,这些方法分别执行对账户余额的存、取和读操作。使用lock(this)来获取锁,确保每个方法执行时都只有一个线程能够访问临界区。需要注意的是,在Withdraw方法中,如果余额不足,则会抛出异常。同时,GetBalance方法也会获取锁,以保证读操作的安全性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#线程同步的三类情景分析 - Python技术站