.NET 中使用 Mutex 进行跨越进程边界的同步 - walterlv
原文:.NET 中使用 Mutex 进行跨越进程边界的同步 - walterlv 2018-12-30 08:41 Mutex 是 Mutual Exclusion 的缩写,是互斥锁,用于防止两个线程同时对计算机上的同一个资源进行访问。不过相比于其他互斥的方式,Mutex 能够跨越线程边界。 与其他线程同步的方式一样,Mutex 也提供对资源的互斥访问;不过 Mutex 使用的系统资源会比 Monitor 更多,而 Monitor 就是实现 C# 中 lock 关键字所用的锁。 用更多的系统资源,带来更强大的功能 —— Mutex 能进行跨越应用程序域边界的封送,能进行跨越进程边界的线程同步。 最简单的 Mutex 的使用方法就是直接 参数中有一个 不过这种方式不能达到跨进程同步的效果,所以实际上本文并不会过多描述这种互斥方式。 要创建跨进程互斥的 Mutex,必须要给 Mutex 指定名称。 使用 在使用这个构造函数重载的时候,第一个参数 注意此程序在两个进程下的运行效果,明明我们等待使用资源的时间间隔只有 50 ms,但实际上等待时间是 1000 ms 左右。在关掉其中一个进程之后,间隔恢复到了 50 ms 左右。 这说明 Mutex 的等待在这里起到了跨进程互斥的作用。 当你需要在是否是第一次创建出来的时候进行一些特殊处理,就使用带 怎样为拥有呢?还记得前面构造函数中的 当一个线程没有拥有这个 Mutex 的时候,需要使用 上面的这段代码,当你第一次运行此进程并且保持此进程不退出的时候并没有什么异样。但是你再启动第二个进程实例的话,就会在 所以如果你不能在一处代码中使用 也就是说,当你需要等待的时候, 本文会经常更新,请阅读原文:
.NET 中使用 Mutex 进行跨越进程边界的同步
本文内容
Mutex 是什么?
简单的 Mutex(不能跨进程互斥)
new
出来,然后使用 Wait
进行等待,使用 ReleaseMutex
进行释放。private readonly Mutex _mutex = new Mutex();
private void UseResource()
{
_mutex.WaitOne();
// 等待一小段时间,假装正在使用公共资源。这里的一段代码在单个进程之内将无法重入。
Thread.Sleep(500);
_mutex.ReleaseMutex();
}
initiallyOwned
参数,如果指定为 true
表示创建这个 Mutex 的线程拥有这个资源(不需要等待),当这个线程调用 ReleaseMutex
之后其他线程的 WaitOne
才会生效。创建跨进程互斥的 Mutex
new Mutex(false, "Walterlv.Mutex")
创建一个命名的互斥锁,以便进行跨进程的资源互斥访问。initiallyOwned
建议的取值为 false
。因为当你指定为 true
时,说明你希望此线程是初始创建此 Mutex
的线程,然而由于你是直接 new
出来的,所以你实质上是无法得知你到底是不是第一个 new
出来的。class Program
{
static async Task Main(string[] args)
{
var program = new Program();
while (true)
{
// 不断地尝试访问一段资源。这样,当多个进程运行的时候,可以很大概率模拟出现资源访问冲突。
program.UseResource();
await Task.Delay(50);
}
}
private void UseResource()
{
var mutex = new Mutex(false, "Walterlv.Mutex");
mutex.WaitOne();
// 正在使用公共资源。
// 这里的一段代码将无法重入,即使是两个不同的进程。
var path = @"C:\Users\lvyi\Desktop\walterlv.log";
Console.WriteLine($"[{DateTime.Now:O}] 开始写入文件……");
File.AppendAllText(path, $"[{DateTime.Now:O}] 开始写入文件……", Encoding.UTF8);
Thread.Sleep(1000);
File.AppendAllText(path, $"[{DateTime.Now:O}] 写入文件完成。", Encoding.UTF8);
Console.WriteLine($"[{DateTime.Now:O}] 写入文件完成。");
mutex.ReleaseMutex();
}
}
createdNew
参数的构造函数。 private void UseResource()
{
-- var mutex = new Mutex(false, "Walterlv.Mutex");
++ var mutex = new Mutex(true, "Walterlv.Mutex", out var createdNew);
-- mutex.WaitOne();
++ // 如果这个 Mutex 是由此处创建出来的,即 createdNew 为 true,说明第一个参数 initiallyOwned 是真的发生了,于是我们就不需要等待。
++ // 反之,当 createdNew 为 false 的时候,说明已经有一个现成的 Mutex 已经存在,我们在这里需要等待。
++ if (!createdNew)
++ {
++ mutex.WaitOne();
++ }
……
mutex.ReleaseMutex();
}
处理异常情况
ApplicationException
mutex.ReleaseMutex();
方法只能被当前拥有它的线程调用,如果某个线程试图调用这个函数,却没有拥有这个 Mutex,就会抛出 ApplicationException
。initiallyOwned
参数吗?就是在指定自己是否是此 Mutex 的拥有者的(实际上我们还需要使用 createdNew
来辅助验证这一点)。WaitOne
来等待获得这个锁。AbandonedMutexException
class Program
{
static async Task Main(string[] args)
{
// 开启一个线程,在那个线程中丢掉获得的 Mutex。
var thread = new Thread(AbandonMutex);
thread.Start();
// 不要让进程退出,否则 Mutex 就会被系统回收。
Console.Read();
}
private static void AbandonMutex()
{
// 获得一个 Mutex,然后就不再释放了。
// 由于此线程会在 WaitOne 执行结束后退出,所以这个 Mutex 就被丢掉了。
var mutex = new Mutex(false, "Walterlv.Mutex");
mutex.WaitOne();
}
}
WaitOne
那里收到一个异常 —— AbandonedMutexException
。try-finally
来确保在获得锁之后一定会释放的话,那么强烈建议在 WaitOne
的时候捕获异常。顺便提醒,try-finally
中不能有异步代码,你可以参见:在有 UI 线程参与的同步锁(如 AutoResetEvent)内部使用 await 可能导致死锁。catch
一下异常。在 catch
完之后,你并不需要再次使用 WaitOne
来等待,因为即便发生了异常,你也依然获得了锁。这一点你可以通过调用 ReleaseMutex
来验证,因为前面我们说了只有拥有锁的线程才可以释放锁。private static void WaitOne()
{
var mutex = new Mutex(false, "Walterlv.Mutex");
try
{
mutex.WaitOne();
}
catch (AbandonedMutexException ex)
{
Console.WriteLine("发现被遗弃的锁");
}
Console.WriteLine("获得了锁");
}
参考资料
https://walterlv.com/post/mutex-in-dotnet.html
,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
进行许可。欢迎转载、使用、重新发布,但务必保留文章署名
吕毅
(包含链接:
https://walterlv.com
),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请
与我联系 (walter.lv@qq.com)
。
.NET 中使用 Mutex 进行跨越进程边界的同步 - walterlv的更多相关文章
- .NET 中使用 Mutex 进行跨越进程边界的同步
Mutex 是 Mutual Exclusion 的缩写,是互斥锁,用于防止两个线程同时对计算机上的同一个资源进行访问.不过相比于其他互斥的方式,Mutex 能够跨越线程边界. 本文内容 Mutex ...
- 在WINDOWS SERVER 上或远程桌面中使用 MUTEX
引用: http://www.cnblogs.com/fg0711/archive/2012/05/03/2480502.html 使用Mutex需要注意的两个细节 可能你已经注意到了,例子中在给Mu ...
- Linux进程和进程边界
1. 进程和线程 2. 手机操作系统的发展 3. 进程的地址空间边界 4. 进程边界的安全围栏: Crash的不可扩延性 5. 进程边界的安全围栏: 全局数据和服务的不可访问性 http://www. ...
- Linux中的两种守护进程stand alone和xinetd
Linux中的两种守护进程stand alone和xinetd --http://www.cnblogs.com/itech/archive/2010/12/27/1914846.html#top 一 ...
- 【翻译】Anatomy of a Program in Memory—剖析内存中的一个程序(进程的虚拟存储器映像布局详解)
[翻译]Anatomy of a Program in Memory—剖析内存中的一个程序(进程的虚拟存储器映像布局详解) . . .
- C#中使用命名管道进行进程通信的实例
原文:C#中使用命名管道进行进程通信的实例 1 新建解决方案NamedPipeExample 在解决方案下面新建两个项目:Client和Server,两者的输出类型均为"Windows 应用 ...
- 使用concurrent.futures模块中的线程池与进程池
使用concurrent.futures模块中的线程池与进程池 线程池与进程池 以线程池举例,系统使用多线程方式运行时,会产生大量的线程创建与销毁,创建与销毁必定会带来一定的消耗,甚至导致系统资源的崩 ...
- Linux 性能分析调优 (四)——案例篇:系统中出现大量不可中断进程和僵尸进程怎么办
之前讲到 CPU 使用率的类型.除了上一节提到的用户 CPU 之外,它还包括系统 CPU(比如上下文切换).等待 I/O 的 CPU(比如等待磁盘的响应)以及中断 CPU(包括软中断和硬中断)等. 在 ...
- 自制Java中的Mutex类
同步问题中,一个很重要的问题是同步的域,什么是同步的域呢?简单以 synchronized 这个关键字来说,就是它所同步的范围.并发编程中很多时候出现的问题没有选好同步范围所导致的.但现有的同步关键字 ...
随机推荐
- android 消息系统Handler、MessageQueue、Looper源代码学习
android消息系统 总体框架如图所看到的 在安卓的消息系统中,每一个线程有一个Looper,Looper中有一个MessageQueue,Handler向这个队列中投递Message,Looper ...
- 去哪网实习总结:用到的easyui组件总结(JavaWeb)
本来是以做数据挖掘的目的进去哪网的,结构却成了系统开发... 只是还是比較认真的做了三个月,老师非常认同我的工作态度和成果.. . 实习立即就要结束了,总结一下几点之前没有注意过的变成习惯和问题,分享 ...
- ajax日期參数格式问题
今天遇到ajax传输日期參数后台无法识别的问题,错误异常例如以下. 从异常中能够看出传输到后台的日期数据格式为Thu Aug 13 2015 19:45:20 GMT+0800 (中国标准时间),这样 ...
- 使用Samba在Linux服务器上搭建共享文件服务
最近我们的小团队需要在服务器上共分出一个共享文件夹用于大家存放公共的资源文档, 大家想啊,这肯定很简单呀,在Windows下面只要创建相关的windows account,共享某个文件夹,把读/写权限 ...
- linux中内存泄漏的检測(一)最简单的方法
什么是内存泄漏 内存泄漏是指程序动态申请的内存在使用完后没有释放,导致这段内存不能被操作系统回收再利用. 比如这段程序,申请了4个字节的空间但没有释放,有4个字节的内存泄漏. #include < ...
- JavaWeb网站技术架构
JavaWeb网站技术架构总结 题记 工作也有几多年了,无论是身边遇到的还是耳间闻到的,多多少少也积攒了自己的一些经验和思考,当然,博主并没有太多接触高大上的分布式架构实践,相对比较零碎,随时补充 ...
- linux下如何获取某一进程占用的物理内存和虚拟内存
首先,ps -A查看你所查看进程的进程号 ps -A 加入进程号为pid 那么使用如下脚本,可以打印该进程使用的虚拟内存和物理内存: root@Storage:/mnt/mtd# cat rss.sh ...
- angular自定义管道
原文地址 https://www.jianshu.com/p/5140a91959ca 对自定义管道的认识 管道的定义中体现了几个关键点: 1.管道是一个带有“管道元数据(pipe metadata) ...
- ios开发核心动画五:图标抖动效果--CAKeyframeAnimation
#import "ViewController.h" #define angle2Rad(angle) ((angle) / 180.0 * M_PI) @interface Vi ...
- JAVA学习路线图---(JAVA1234) 分类: B1_JAVA 2013-10-05 10:22 502人阅读 评论(1) 收藏
转自:http://blog.csdn.net/pplcheer/article/details/12276999 第一阶段-Java基础 这一阶段很重要,关系到你后面阶段的学习,所以务 ...