C#中如何发现Mutex被获取了?
如何从 C# 中的互斥体句柄中找到已获取互斥体?
当 mutex.WaitOne(timeout)
超时时,它返回 false
。但是,我如何从互斥体句柄中找到它? (也许使用 p/invoke。)
更新:
public class InterProcessLock : IDisposable
{
readonly Mutex mutex;
public bool IsAcquired { get; private set; }
public InterProcessLock(string name, TimeSpan timeout)
{
bool created;
var security = new MutexSecurity();
security.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
mutex = new Mutex(false, name, out created, security);
IsAcquired = mutex.WaitOne(timeout);
}
#region IDisposable Members
public void Dispose()
{
if (IsAcquired)
{
mutex.ReleaseMutex();
IsAcquired = false;
}
}
#endregion
}
目前,我正在使用自己的属性 IsAcquired
来确定是否应该释放互斥体。不是必需的,但更清楚的是,不使用 IsAcquired 属性表示的信息的辅助副本,而是直接询问互斥体是否由我获取。因为如果我没有获取它,调用 mutex.ReleaseMutex()
会抛出异常。
(通过获得状态,我的意思是当我拥有互斥体时,互斥体处于未发出信号状态。)
(编辑:我已添加IsAcquired = false;
感谢 mattdekrey 的帖子。)
How can I find from mutex handle in C# that a mutex is acquired?
When mutex.WaitOne(timeout)
timeouts, it returns false
. However, how can I find that from the mutex handle? (Maybe using p/invoke.)
UPDATE:
public class InterProcessLock : IDisposable
{
readonly Mutex mutex;
public bool IsAcquired { get; private set; }
public InterProcessLock(string name, TimeSpan timeout)
{
bool created;
var security = new MutexSecurity();
security.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
mutex = new Mutex(false, name, out created, security);
IsAcquired = mutex.WaitOne(timeout);
}
#region IDisposable Members
public void Dispose()
{
if (IsAcquired)
{
mutex.ReleaseMutex();
IsAcquired = false;
}
}
#endregion
}
Currently, I am using my own property IsAcquired
to determine whether I should release a mutex. Not essential but clearer, would be not to use a secondary copy of the information represented by IsAcquired
property, but rather to ask directly the mutex whether it is acquired by me. Since calling mutex.ReleaseMutex()
throws an exception if it is not acquired by me.
(By acquired state I mean that the mutex is in not-signaled state when I am owning the mutex.)
(EDIT: I have added IsAcquired = false;
thanks to mattdekrey's post.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
之所以没有干净的方法来做到这一点,是因为这不是一个好主意,而且原因是,当您依赖这种类型的逻辑时,竞争条件很容易引入。所以你的设计需要改变。
首先,您不应该在构造函数中获取锁。将此类转换为返回正确初始化的互斥对象的工厂。这样你就可以知道你是否获得了锁。
不要依赖 Dispose 来释放锁,这会导致死锁缠身的代码难以维护。使用 try/finally 块来确保它被释放。
超时有点粗略。只有在未获取锁时才使用超时才被视为正常操作。无法获取锁通常是一个错误,而仅仅通过超时来避免它会隐藏该错误。如果您需要超时,请考虑使用事件(可能是 AutoResetEvent),这可能更合适。
The reason there is no clean way to do this is because it is not a good idea and the reason is because race conditions are -very- easy to introduce when you rely on this type of logic. So your design needs to change.
First, you should not acquire a lock in a constructor. Turn this class into a factory that returns a properly initialized mutex object. That way you can know if you acquired the lock or not.
DO NOT rely on Dispose to release locks, this is asking for deadlock ridden code that is hard to maintain. Use a try/finally block to ensure it is released.
Timeouts are a bit sketchy. Only use timeouts when not acquiring the lock would be considered normal operation. Not being able to acquire the lock is usually a bug and merely avoiding it with timeouts hides the bug. If you need timeouts, consider using an event (maybe AutoResetEvent), this may be more appropriate.
您可能会发现,
Mutex
类上没有公共成员:http://msdn.microsoft.com/en-us/library /system.threading.mutex_members.aspx
也没有公共本机函数:
http://msdn.microsoft.com/en -us/library/ms686360%28v=VS.85%29.aspx
但是,有一些未记录/不受支持的函数,特别是在
ntdll.dll
中。这些允许访问系统对象。但是,这些功能可能会在未来版本的操作系统中发生变化或不可用。所以,答案是:使用常规手段是不可能的。
As you may found, there are no public members on
Mutex
class:http://msdn.microsoft.com/en-us/library/system.threading.mutex_members.aspx
There is also no public native functions for that:
http://msdn.microsoft.com/en-us/library/ms686360%28v=VS.85%29.aspx
However, there are some undocumented/unsupported functions especially in
ntdll.dll
. These allow accessing system objects. However, these functions may change or not be available in future versions of operating system.So, the answer is: It is not possible using conventional means.
好吧,这并不完全是您所要求的,但我认为它可以解决您的问题:为什么不专门针对互斥体被其他人获取时发生的异常添加一些错误处理呢?
Well, it's not exactly what you're asking for, but I think it would solve your problem: why not just add some error handling specifically for the exception that occurs if the Mutex is aquired by someone else?
为什么你不能使用
Mutex.OpenExisting
编辑
我猜测其中一些。
看起来你正在尝试开发一个 API。您在 API 中提供的项目之一是 InterProcessLock。
我假设您正在跨线程共享一个集合,并且您正在使用互斥体来确保一次只有一个操作。
我会重新考虑这个设计。如果我从未将 InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0)) 包装在 using 中怎么办?不会调用 Dispose。如果用户根本不调用 Dispose 会怎样?
将会有一个废弃的互斥锁
来自 MSDN
如果您试图保护您的用户,您可能希望通过控制他们的互斥体来帮助他们,这样他们就不必担心它。
一个可能的例子是
这是一种可能的方式。这完全取决于目标是什么。我不会依赖最终用户来调用 Dispose,因为互斥锁是一种操作系统构造。如果名称不明确,它可能会影响使用相同互斥体名称的其他进程。
Why can't you use
Mutex.OpenExisting
EDIT
I am guessing some of this.
It seems like you are trying to develop an API. One of the items you are offering in your API is an InterProcessLock.
I am going to assume you are sharing a collection across threads and you are using the Mutex to make sure only one operation is on it a time.
I would reconsider this design. What if I never wraped
InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0))
in a using? Dispose would not be called. What if the user never calls the Dispose at all?There would be an abandoned Mutex
From MSDN
If you are trying to protect your users you might want to help them by controlling the Mutex for them so they never have to worry about it.
A possible example is
This is one possible way. It all depends on what the goal is. I would NOT relay on the final user to call a Dispose since a Mutex is an operating system construct. And if the name is not unquie it could effect other processes using the same mutex name.
这不会有利于问题的原始发布者,但就这样。
虽然我并不反对其他关于正确使用互斥锁的发帖者的观点,但我有一个应用程序,我需要测试某人是否拥有互斥锁,而无需我自己取得所有权。正如其他人提到的,唯一的方法是使用来自 ntdll.dll 的未记录的 NtQueryMutant 系统调用。我为 Mutex 类创建了一个扩展方法,可以像这样使用:
这是实现
This won’t benefit the original poster of the question but here it goes.
While I do not disagree with other posters on proper use of Mutexes, I had an application where I needed to test whether someone owns a mutex without taking ownership myself. As mentioned by others the only way is to use an undocumented NtQueryMutant system call from ntdll.dll. I created an extension method for the Mutex class that can be used like this:
And here is the implementation
.NET Mutex 类是本机互斥体包装器,它提供了与本机互斥体 API 相同的可能性(除了等待不同类型的可等待对象的数量)。如果您想在不阻塞的情况下获取互斥锁,请调用mutex.WaitOne(0)。使用 PInvoke,您可以调用 WaitForSingleObject,得到相同的结果。
.NET Mutex class is native mutex wrapper, which gives the same possibilities, as native mutex API (except waiting for number of waitable objects of different type). If you want to acquire mutex without blocking, call mutex.WaitOne(0). Using PInvoke, you can call WaitForSingleObject, with the same result.
如果您确实尝试进行进程间锁定,顾名思义,您将需要一种方法来检测互斥锁是否实际上已被获取,对吗?我不确定如果没有
IsAcquired
属性,如何确保使用InterProcessLock
的代码被锁定。 (此外,为了防止程序员意外调用 Dispose 两次,我在Dispose
方法中将IsAcquired
设置为false
。)我自己实现了同样的事情(因为我更喜欢使用 using 块而不是 try-finally 只是为了释放互斥体),而是在超过超时时抛出异常,如果我正确地记得该项目,则不会调用处置方法。
编辑:
在构造函数中抛出异常的额外好处:您的临界区也完全避免了,并且您可以在 catch 块中执行错误处理,无论如何,这可能包括与您的临界区相同的方法调用,尽管我个人认为这是一种不好的做法。
经过进一步思考,您可以在处置中使用以下内容,而不是使用另一个答案中指定的 try ... catch :
锁定互斥体感觉有点讽刺,但是你已经有了。虽然我完全同意您不应该依赖于 IDisposable 接口的文档而调用 Dispose,但我认为拥有一个由
using() { }块。
If you're really trying to do an inter-process lock, as the name implies, you will want a way to detect if the Mutex has actually been acquired anyway, correct? I'm not sure how your code that uses your
InterProcessLock
would be ensured to be locked if there was noIsAcquired
property. (Also, to protect against programmers who accidentally call Dispose twice, I'd set theIsAcquired
tofalse
in yourDispose
method.)I've implemented the same thing myself (because I much prefer the using block to a try-finally just to release the mutex) and instead threw an exception when the timeout was exceeded, which, if I'm remembering the project correctly, did not call the Dispose method.
Edit:
Added benefit of throwing the exception in the constructor: your critical section is also completely avoided, and you can perform error handling in the
catch
block, which could include the same method call your critical section had, anyway, though I personally would consider that a bad practice.Upon further reflection, rather than using
try ... catch
as specified in another answer, you could use the following on your dispose:It feels a little ironic to
lock
a mutex, but there you have it. While I totally agree that you shouldn't rely on Dispose being called because of the documentation with the IDisposable interface, I think it is incredibly convenient to have an inter-process critical section indicated by ausing() { }
block.