编辑
2025-09-24
C#
00

目录

摘要
正文
一个例子
信号量(Semaphore)

摘要

在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方法发送信号到等待线程以继续其工作。

一个例子

image.png

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();//发送信号 }

image.png

这样好像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(); }

信号量(Semaphore)

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(); // 顾客完成使用,释放柜台资源 }

image.png

在这个例子中,我们首先创建了一个信号量_semaphore,它的初始计数和最大计数都设置为3,表示最多允许3个线程同时访问受限资源(银行柜台)。然后,我们模拟了10个顾客(线程)尝试使用银行柜台。

每个顾客(线程)在尝试使用银行柜台之前,都会调用_semaphore.WaitOne()方法。这个方法会尝试减少信号量的计数。如果计数大于0,表示有柜台可用,当前线程将继续执行并模拟使用柜台的过程。如果计数为0,表示没有柜台可用,当前线程将被阻塞,直到某个其他线程调用_semaphore.Release()释放柜台(即增加信号量的计数),当前线程才有机会继续执行。

每个顾客(线程)使用完柜台后,都会调用_semaphore.Release()方法来释放柜台,这样其他正在等待的顾客(线程)就有机会使用柜台了。

通过这个例子,你可以看到信号量如何用于控制对有限资源的并发访问。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!