news 2026/7/5 3:53:57

深入浅出多线程系列之五:一些同步构造(上篇)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入浅出多线程系列之五:一些同步构造(上篇)

Mutex 就像一个C# lock一样,不同的是它可以跨进程.

进入和释放一个Mutex要花费几毫秒,大约比C#的lock慢50倍。

使用一个Mutex的实例,调用WaitOne方法来获取锁,ReleaseMutex方法来释放锁。

因为Mutex是跨进程的,所以我们可以使用Mutex来检测程序是否已经运行。

publicstaticvoidMainThread()
{
using(var mutex=newMutex(false,"LoveJenny OneAtATimeDemo"))
{
if(!mutex.WaitOne(TimeSpan.FromSeconds(3),false))
{
Console.WriteLine(
"只能运行一个应用程序!");
return;
}

RunProgram();
}
}

2:Semaphore:

一个Semaphore就像一个酒吧一样,通过门卫来限制它的客人,一旦到达限制,没有人可以进入,

人们会在门外乖乖的排队,一旦有一个人离开酒吧,排队中的人就可以进入了一个了。

下面是个例子:

classTheClub
{
//只能容纳三个人的酒吧
staticSemaphoreSlim _sem=newSemaphoreSlim(3);

publicstaticvoidMainThread()
{
for(inti=1; i<=5; i++)
newThread(Enter).Start(i);//有5个人向进入
}
staticvoidEnter(objectid)
{
Console.WriteLine(id
+"想要进入了");
_sem.Wait();
Console.WriteLine(id
+"已经进入了!");
Thread.Sleep(
1000*(int)id);
Console.WriteLine(id
+"离开了?");
_sem.Release();
}
}

3:AutoResetEvent

一个AutoResetEvent就像十字转门一样,插入一张票就让一个人通过,”Auto”代表门会自动的关上。

在十字门外面的人可以调用WaitOne方法来阻塞,等待。一旦有人插入了票(调用Set方法),就可以让外面等待的人(调用WaitOne方法的线程)通过了。

创建AutoResetEvent有一个参数。

staticEventWaitHandle_waitHandle =newAutoResetEvent(false);

其中false在msdn的解释是:初始状态为非终止,

按照我个人的理解false代表了十字转门非终止,所以可以正常的进入,等待。

而如果是true的话:初始状态为终止,也就是代表已经调用了Set了,

就是说十字转门已经停止了,所以接下来如果有人调用了WaitOne方法,这个调用WaitOne方法的人直接就可以进入了,不需要再插入票(不需要调用Set)了,之后的调用和false一致,这一点可以认为AutoResetEvent具有记忆功能,它记住了上次门是打开的状态。所以调用waitone方法可以进入。

classThreadAutoResetEvent
{
staticEventWaitHandle _waitHandle=newAutoResetEvent(false);

publicstaticvoidMainThread()
{
newThread(Waiter).Start();
Thread.Sleep(
2000);
_waitHandle.Set();
}

staticvoidWaiter()
{
Console.WriteLine(
"Waiting...");
_waitHandle.WaitOne();
Console.WriteLine(
"Notified");
}
}

很简单,Waiter执行到Waiting…后,就开始调用WaitOne了,所以在门外排队等待。

而主线程在睡了两秒后,开始插入一张票(Set).所以Waiter就继续执行,所以打印Notified

接下来我们使用AutoResetEvent来模拟实现生产消费问题:

classProducerConsumerQueue:IDisposable
{
EventWaitHandle _wh
=newAutoResetEvent(false);
Thread _worker;
readonlyobject_locker=newobject();
Queue
<string>_tasks=newQueue<string>();

publicProducerConsumerQueue()
{
//创建并启动工作线程
_worker=newThread(Work);
_worker.Start();
}

publicvoidEnqueueTask(stringtask)
{
lock(_locker) _tasks.Enqueue(task);
_wh.Set();
//一旦有任务了,唤醒等待的线程
}

publicvoidDispose()
{
EnqueueTask(
null);
_worker.Join();
//等待_worker线程执行结束
_wh.Close();
}

voidWork()
{
while(true)
{
stringtask=null;
lock(_locker)
{
if(_tasks.Count>0)
{
task
=_tasks.Dequeue();
if(task==null)
return;
}
if(task!=null)//如果有任务的话,执行任务
{
Console.WriteLine(
"Performing task:"+task);
Thread.Sleep(
1000);
}
else//否则阻塞,去睡觉吧
{
_wh.WaitOne();
}
}
}
}
}

主线程调用如下:

publicstaticvoidMain()
{
using(ProducerConsumerQueue q=newProducerConsumerQueue())
{
q.EnqueueTask(
"Hello");
for(inti=0; i<10; i++) q.EnqueueTask("Say"+i);
q.EnqueueTask(
"Goodbye!");
}
}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 3:51:31

当庄稼开始“说话”:百格科技如何用数据重写农业的底层逻辑

在甘肃会宁的一间蔬菜大棚里&#xff0c;老农张卫国做了一个他父辈想都不敢想的决定——在连续一周的阴雨天里&#xff0c;他没有像往常一样凭经验减少浇水量&#xff0c;而是掏出手机看了眼屏幕上的数字&#xff0c;然后打开了滴灌阀门。这个看似反直觉的操作&#xff0c;源于…

作者头像 李华
网站建设 2026/7/5 3:47:44

2026自动化测试工具全景图

2026自动化测试工具全景图 — 选型不再迷茫面对Selenium、Playwright、Cypress、Pytest、Appium、Hypium……一大堆工具名&#xff0c;该选哪个&#xff1f;这篇文章用一张决策树帮你3分钟搞定选型&#xff0c;附11年实战经验加持的横评对比表。一、为什么写这篇文章&#xff1…

作者头像 李华
网站建设 2026/7/5 3:45:16

Firefox 密码取证:利用 firepwd.py 解密 logins.json 与 key4.db 的 5 个关键步骤

Firefox密码取证实战&#xff1a;从logins.json与key4.db解密到安全防护的完整指南浏览器密码存储机制深度解析现代浏览器采用复杂的加密体系保护用户密码&#xff0c;而作为安全研究人员或数字取证从业者&#xff0c;理解这套机制的工作原理至关重要。Firefox浏览器使用三重防…

作者头像 李华
网站建设 2026/7/5 3:42:07

清理迁移vscode-cpptools文件夹占用的C盘空间

目录解决方法直接删除修改路径方法一&#xff1a;通过设置界面配置方法二&#xff1a;通过 settings.json 文件配置方法三&#xff1a;使用符号链接C盘根目录下的C:\Users\username\AppData\Local\Microsoft\vscode-cpptools 文件夹占用了大量空间。 这是 VS Code 中 C/C 扩展 …

作者头像 李华