互斥锁/监视器不能用于c#线程同步
本文关键字:用于 线程 同步 不能 监视器 | 更新日期: 2023-09-27 18:03:28
我试图只允许一个Thread
从object
访问非静态方法。我并不真正创建线程,而是使用任务。我试图将该方法锁定到一个用法,但它只是不起作用,我首先尝试了一个静态Mutex
对象,然后是一个非静态Mutex
对象,但即使有一个简单的监视器,它也不起作用。
private async void LoadLinkPreview()
{
try
{
// TODO: FEHLERTRÄCHTIG!
Monitor.Enter(_loadLinkMonitor);
Debug.WriteLine(DateTime.Now + ": Entered LinkPreview");
// Code ...
// There are also "await" usages here, don't know if this matters
}
catch { }
finally
{
Monitor.Exit(_loadLinkMonitor);
Debug.WriteLine(DateTime.Now + ": Left LinkPreview");
}
}
,这是控制台显示的内容:
3/12/2013 6:10:03 PM: Entered LinkPreview
3/12/2013 6:10:05 PM: Entered LinkPreview
3/12/2013 6:10:05 PM: Left LinkPreview
3/12/2013 6:10:06 PM: Left LinkPreview
当然,有时它会按照想要的顺序工作,但我希望它总是工作。有人能帮帮我吗?
编辑:我意识到控制台显示可能是想要的行为,因为"左"WriteLine()
是在监视器退出后,但我现在切换了它们,可以确认肯定有两个线程同时工作!还有一个更明显的控制台结果:
3/12/2013 6:26:15 PM: Entered LinkPreview
3/12/2013 6:26:15 PM: Entered LinkPreview
2 had an error! // Every time the method is used I count up now, this confirms that the second
// time the method is called the error is produced, which would not happen if the monitor
// is working because I have an if statement right upfront ...
3/12/2013 6:26:17 PM: Left LinkPreview
3/12/2013 6:26:18 PM: Left LinkPreview
实际上有可能你的代码按预期工作,而你的问题只是与显示有关。
问题在这里:
Monitor.Exit(_loadLinkMonitor);
Debug.WriteLine(DateTime.Now + ": Left LinkPreview");
在退出监视器后写出时间戳。请记住,等待进入监视器的其他代码可以在调用Exit
后立即开始执行。因此,当前线程可以调用exit,然后在Debug
调用之前,另一个线程可以Enter
监视器并将其写入"enter"行。
如果您将代码更改为:
Debug.WriteLine(DateTime.Now + ": Left LinkPreview");
Monitor.Exit(_loadLinkMonitor);
那么它将不会改变行为,但您将删除日志错误。如果仍然乱序,那么您的代码中确实有问题。
如果你有一个async
方法,你应该避免调用阻塞等待的方法,比如Monitor.Enter
。如果你使用SemaphoreSlim
,你可以使用它的WaitAsync
方法,以确保该方法不会阻塞。它看起来像这样:
private SemaphoreSlim semaphore = new SemaphoreSlim(1);
private async void LoadLinkPreview()
{
try
{
await semaphore.WaitAsync();
Debug.WriteLine(DateTime.Now + ": Entered LinkPreview");
await Task.Delay(2000);//placeholder for real work/IO
}
finally
{
Debug.WriteLine(DateTime.Now + ": Left LinkPreview");
semaphore.Release();
}
}
编辑:抛弃所有这些。答案是:为什么我不能使用'await'在锁语句体中的操作符?
你不应该做你想做的事情,使用"await"本质上是一个"lock"语句。
不能在async
方法中使用Monitor
等标准多线程原语。如果你使用lock
语句,c#编译器会阻止你,但如果你通过Monitor.Enter
"手动"执行,那么你最终会搬起石头砸自己的脚。
如果你稍微想一下,这是完全有道理的。当async
方法产生时会发生什么?它返回一个未完成的Task
,并且允许其他代码在等待时运行。这完全违背了锁的目的,锁的目的是在锁被持有时防止其他代码运行。也有一些情况(例如ASP。. NET或线程池上下文),其中async
方法在另一个线程上恢复;在这种情况下,一个线程获取一个锁,另一个线程释放它。精神错乱!猫和狗住在一起!
内置的。net解决方案是SemaphoreSlim
,它作为锁/信号量工作(尽管它没有内置IDisposable
用于using
)。Stephen Toub写了一个关于async
协调原语的博客系列,我在我的AsyncEx库中实现了一个完整的套件,可以通过NuGet获得。