在C#中,线程同步是一种机制,用于控制多个线程访问共享资源的顺序,以避免数据竞争和不一致性问题。C#提供了多种线程同步技术,包括但不限于锁(lock)、监视器(Monitor)、信号量(Semaphore)、事件(EventWaitHandle、AutoResetEvent、ManualResetEvent)和互斥体(Mutex)。
AutoResetEvent
AutoResetEvent是.net线程简易同步方法中的一种。AutoResetEvent 常常被用来在两个线程之间进行信号发送
两个线程共享相同的AutoResetEvent对象,线程1可以通过调用AutoResetEvent对象的WaitOne()方法进入等待状态,然后另外一个线程2通过调用AutoResetEvent对象的Set()方法取消等待的状态。
AutoResetEvent如何工作的
在内存中保持着一个bool值,如果bool值为False,则使线程阻塞,反之,如果bool值为True,则使线程退出阻塞。当我们创建AutoResetEvent对象的实例时,我们在函数构造中传递默认的bool值,以下是实例化AutoResetEvent的例子。
WaitOne 方法
该方法阻止当前线程继续执行,并使线程进入等待状态以获取其他线程发送的信号。WaitOne将当前线程置于一个休眠的线程状态。WaitOne方法收到信号后将返回True,否则将返回False。
Set 方法
AutoResetEvent Set方法发送信号到等待线程以继续其工作。
C#int Data = 0;
AutoResetEvent autoReset = new AutoResetEvent(false);
private void btnTest_Click(object sender, EventArgs e)
{
Thread t=new Thread(() => { ReturnData(); });
t.Start();
lblValue.Text = "等待中...";
autoReset.WaitOne();//等待线程完成
lblValue.Text = $"取得Value{Data}";
}
private void ReturnData()
{
Thread.Sleep(5000);
Data=new Random().Next(1,999);
autoReset.Set();//发送信号
}
这样好像Sleep把窗口要冻结5秒,我们修改一下。
C#int Data = 0;
AutoResetEvent autoReset = new AutoResetEvent(false);
private void btnTest_Click(object sender, EventArgs e)
{
Thread thread = new Thread(Do);
thread.Start();
}
private void Do()
{
Thread t = new Thread(() => { ReturnData(); });
t.Start();
this.BeginInvoke(() =>
{
lblValue.Text = "等待中...";
});
autoReset.WaitOne();
this.BeginInvoke(() =>
{
lblValue.Text = $"取得Value{Data}";
});
}
private void ReturnData()
{
Thread.Sleep(5000);
Data = new Random().Next(1, 999);
autoReset.Set();
}
C#// 创建一个信号量实例,假设有3个银行柜台,即最多允许3个线程同时访问
private static Semaphore _semaphore = new Semaphore(3, 3);
static void Main(string[] args)
{
for (int i = 1; i <= 10; i++) // 模拟10个顾客
{
Thread customerThread = new Thread(new ParameterizedThreadStart(Customer));
customerThread.Start(i);
}
}
private static void Customer(object customerId)
{
Console.WriteLine($"顾客{customerId} 正在等待使用银行柜台");
_semaphore.WaitOne(); // 顾客尝试访问柜台,如果没有空闲的柜台,顾客将等待
Console.WriteLine($"顾客{customerId} 开始使用银行柜台");
Thread.Sleep(new Random().Next(1000, 5000)); // 模拟顾客使用银行柜台的时间
Console.WriteLine($"顾客{customerId} 完成使用,离开银行柜台");
_semaphore.Release(); // 顾客完成使用,释放柜台资源
}
在这个例子中,我们首先创建了一个信号量_semaphore
,它的初始计数和最大计数都设置为3,表示最多允许3个线程同时访问受限资源(银行柜台)。然后,我们模拟了10个顾客(线程)尝试使用银行柜台。
每个顾客(线程)在尝试使用银行柜台之前,都会调用_semaphore.WaitOne()
方法。这个方法会尝试减少信号量的计数。如果计数大于0,表示有柜台可用,当前线程将继续执行并模拟使用柜台的过程。如果计数为0,表示没有柜台可用,当前线程将被阻塞,直到某个其他线程调用_semaphore.Release()
释放柜台(即增加信号量的计数),当前线程才有机会继续执行。
每个顾客(线程)使用完柜台后,都会调用_semaphore.Release()
方法来释放柜台,这样其他正在等待的顾客(线程)就有机会使用柜台了。
通过这个例子,你可以看到信号量如何用于控制对有限资源的并发访问。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!