如何从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()
会引发异常,如果我没有获取它。
(通过获取状态我的意思是当我拥有互斥锁时,互斥锁处于 not-signaled 状态。)
(编辑:由于mattdekrey's post,我添加了IsAcquired = false;
。)
答案 0 :(得分:13)
没有干净方法的原因是因为这不是一个好主意,原因是因为当你依赖这种类型的逻辑时,竞争条件很容易引入。所以你的设计需要改变。
首先,您不应该在构造函数中获取锁。将此类转换为返回正确初始化的互斥对象的工厂。这样你就可以知道你是否获得了锁。
不要依赖Dispose来释放锁,这就是要求难以维护的死锁代码。使用try / finally块确保它被释放。
超时有点粗略。仅在未获取锁定时使用超时将被视为正常操作。无法获取锁通常是一个错误,只是通过超时避免它隐藏了错误。如果您需要超时,请考虑使用事件(可能是AutoResetEvent),这可能更合适。
答案 1 :(得分:4)
如您所见,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
中。这些允许访问系统对象。但是,这些功能可能会在未来版本的操作系统中更改或不可用。
所以,答案是:使用传统手段是不可能的。
答案 2 :(得分:2)
为什么不能使用Mutex.OpenExisting
try
{
Mutex foundMutex = Mutex.OpenExisting("MyTestingMutex");
// Found Mutex
foundMutex.ReleaseMutex();
}
catch (System.Threading.WaitHandleCannotBeOpenedException)
{
// System.Threading.WaitHandleCannotBeOpenedException:
// The named mutex does not exist.
}
<强> 修改 强>
我猜其中一些。
您似乎正在尝试开发API。您在API中提供的项目之一是InterProcessLock。
我将假设您正在跨线程共享一个集合,并且您正在使用Mutex来确保一次只有一个操作。
using (InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0)))
{
if(myLock.IsAcquired)
{
// I have control then I can delete, add to the collection.
}
}
我会重新考虑这个设计。如果我从未在使用中包裹InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0))
怎么办?不会调用Dispose。如果用户根本不打电话给Dispose怎么办?
会有一个废弃的互斥锁
来自MSDN
注意 废弃的互斥锁通常表示代码中存在严重错误。当线程退出但未释放互斥锁时,受互斥锁保护的数据结构可能不处于一致状态。如果可以验证数据结构的完整性,请求互斥锁所有权的下一个线程可以处理此异常并继续。
如果您试图保护您的用户,您可能希望通过为他们控制互斥锁来帮助他们,这样他们就不用担心了。
一个可能的例子是
public static bool PerformLockedProcess(Action process, string commonLockName, TimeSpan timeout)
{
Mutex mutex = null;
// Get the Mutex for the User
try
{
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, commonLockName, out created, security);
bool acquired = mutex.WaitOne(timeout);
if (acquired)
{
process();
return true;
}
return false;
}
finally
{
// Make sure we do not abandon the Mutex
if (mutex != null)
{
try
{
mutex.ReleaseMutex();
}
catch (ApplicationException)
{
// In case that failes
}
}
}
}
这是一种可能的方式。这一切都取决于目标是什么。因为Mutex是一个操作系统构造,所以我不会在最终用户上中继调用Dispose。如果名称不是unquie,则可能会影响使用相同互斥锁名称的其他进程。
答案 3 :(得分:1)
嗯,这不完全是你所要求的,但我认为它可以解决你的问题:为什么不只是添加一些错误处理专门针对在其他人获得Mutex时发生的异常?
public void Dispose()
{
if (IsAcquired)
try
{ mutex.ReleaseMutex(); }
catch (System.Threading.SynchronizationLockException)
{
// Handle the exception, assuming you need to do anything.
// All other exceptions would still be passed up the stack.
}
}
答案 4 :(得分:1)
这不会使问题的原始海报受益,但在这里。
虽然我不同意其他正确使用互斥锁的海报,但我有一个应用程序,我需要测试某人是否拥有互斥锁而不自行取得所有权。正如其他人所提到的,唯一的方法是使用来自ntdll.dll的未记录的NtQueryMutant系统调用。我为Mutex类创建了一个扩展方法,可以像这样使用:
bool createdNew = true;
var m = new Mutex(false, MutexName, out createdNew);
if ( m != null)
{
int currentCount;
bool ownedByCaller, abandonedState;
if (m.TryQuery(out currentCount, out ownedByCaller, out abandonedState))
{
Console.WriteLine(string.Format("Created New: {3}, Count: {0}, OwvedByMe: {1}, Abandoned: {2}",
currentCount, ownedByCaller, abandonedState, createdNew));
}
m.Close();
}
这是实施
public static class MutexExtensionMethods
{
public static bool TryQuery(this Mutex m, out int currentCount, out bool ownedByCaller, out bool abandonedState)
{
currentCount = -1;
ownedByCaller = abandonedState = false;
try
{
var handle = m.SafeWaitHandle;
if (handle != null)
{
var h = handle.DangerousGetHandle();
MutantBasicInformation mbi;
int retLength;
var ntStatus = NtQueryMutant(
h,
MutantInformationClass.MutantBasicInformation,
out mbi,
Marshal.SizeOf(typeof(MutantBasicInformation)),
out retLength);
GC.KeepAlive(handle); // Prevent "handle" from being collected before NtQueryMutant returns
if (ntStatus == 0)
{
currentCount = mbi.CurrentCount;
ownedByCaller = mbi.OwnedByCaller;
abandonedState = mbi.AbandonedState;
return true;
}
}
}
catch
{
}
return false;
}
#region NTDLL.DLL
[DllImport("ntdll.dll")]
public static extern uint NtQueryMutant(
[In] IntPtr MutantHandle,
[In] MutantInformationClass MutantInformationClass,
[Out] out MutantBasicInformation MutantInformation,
[In] int MutantInformationLength,
[Out] [Optional] out int ReturnLength
);
public enum MutantInformationClass : int
{
MutantBasicInformation
}
[StructLayout(LayoutKind.Sequential)]
public struct MutantBasicInformation
{
public int CurrentCount;
[MarshalAs(UnmanagedType.U1)]
public bool OwnedByCaller;
[MarshalAs(UnmanagedType.U1)]
public bool AbandonedState;
}
#endregion
}
答案 5 :(得分:0)
.NET Mutex类是本机互斥包装器,它提供与本机互斥API相同的可能性(除了等待不同类型的可等待对象的数量)。如果要在不阻塞的情况下获取互斥锁,请调用mutex.WaitOne(0)。使用PInvoke,您可以调用WaitForSingleObject,结果相同。
答案 6 :(得分:0)
如果您真的想要进行进程间锁定,顾名思义,您将需要一种方法来检测Mutex是否实际上已被获取,是否正确?如果没有InterProcessLock
属性,我不确定如何确保使用IsAcquired
的代码被锁定。 (另外,为防止意外调用Dispose两次的程序员,我会在IsAcquired
方法中将false
设置为Dispose
。
我自己实现了同样的事情(因为我更喜欢使用块来尝试 - 最后只是为了释放互斥锁)而是在超出超时时抛出异常,如果我记得这个项目没错,没有调用Dispose方法。
修改:
添加了在构造函数中抛出异常的好处:您的关键部分也完全被避免,并且您可以在catch
块中执行错误处理,无论如何,这可能包括与您的关键部分相同的方法调用,尽管我个人会认为这是一种不好的做法。
在进一步思考后,您可以在处理中使用以下内容,而不是使用另一个答案中指定的try ... catch
:
public void Dispose()
{
if (IsAcquired)
{
lock (mutex)
{
mutex.ReleaseMutex();
IsAcquired = false;
}
}
}
lock
互斥锁感觉有点讽刺,但是你有它。虽然我完全同意你不应该因为带有IDisposable接口的文档而依赖于调用Dispose,但我认为让using() { }
块表示一个进程间关键部分非常方便。