t3c*_*b0t 1 c# mutex idisposable thread-safety
我试图Mutex用这样的IDisposable类包装一个:
public class NamedMutex : IDisposable
{
private static readonly object _syncLock = new object();
private readonly Mutex _namedMutex;
private readonly bool _createdNew;
public NamedMutex(string name)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
//lock (_syncLock)
{
_namedMutex = new Mutex(initiallyOwned: false, name: name, createdNew: out _createdNew);
}
_namedMutex.WaitOne();
}
public void Dispose()
{
//lock (_syncLock)
{
//if (_createdNew)
_namedMutex.ReleaseMutex();
_namedMutex.Dispose();
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如你从注释掉的代码中看到的那样,我已经尝试了几乎所有我能想到的工作,但要么是我的测试错误,要么是上述实现不对,因为测试要么永远不会结束(可能是死锁我无法识别或与未同步的异常崩溃).
这是我为LINQPad改编的测试:
void Main()
{
var sw = Stopwatch.StartNew();
var task1 = Task.Run(async () =>
{
using (new NamedMutex("foo"))
{
Console.WriteLine(3);
await Task.Delay(TimeSpan.FromSeconds(3));
}
});
var task2 = Task.Run(async () =>
{
using (new NamedMutex("foo"))
{
Console.WriteLine(2);
await Task.Delay(TimeSpan.FromSeconds(2));
}
});
Task.WaitAll(task1, task2);
//Assert.IsTrue(sw.Elapsed.TotalSeconds >= 5);
sw.Elapsed.Dump(); // LINQPad
}
Run Code Online (Sandbox Code Playgroud)
这是因为await.在您之后,您await Task.Delay(..)可能不再处于await声明之前的同一个线程中.因此,在某些情况下,您试图从不拥有它的线程中释放您的互斥锁 - 因此您的问题.通过在await之前和之后编写当前线程,可以很容易地验证:
class Program {
public static void Main() {
while (true) {
var sw = Stopwatch.StartNew();
var task1 = Task.Run(async () => {
using (new NamedMutex("foo")) {
Console.WriteLine("first before await: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(TimeSpan.FromSeconds(2));
Console.WriteLine("first after await: " + Thread.CurrentThread.ManagedThreadId);
}
});
var task2 = Task.Run(async () => {
using (new NamedMutex("foo")) {
Console.WriteLine("second before await: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(TimeSpan.FromSeconds(1));
Console.WriteLine("second after await: " + Thread.CurrentThread.ManagedThreadId);
}
});
Task.WaitAll(task1, task2);
//Assert.IsTrue(sw.Elapsed.TotalSeconds >= 5);
Console.WriteLine(sw.Elapsed);
}
}
}
Run Code Online (Sandbox Code Playgroud)