3

我试图只允许一个人Threadobject. 我并没有真正创建线程,而是使用任务。我试图将该方法锁定为一种用法,但它不起作用,我首先尝试使用静态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
4

3 回答 3

4

实际上,您的代码可能按预期工作,而您的问题只是显示。

问题在这里:

 Monitor.Exit(_loadLinkMonitor);
 Debug.WriteLine(DateTime.Now + ": Left LinkPreview");

退出监视器写出时间戳。请记住,等待进入监视器的其他代码可以在Exit调用后立即开始执行。因此,当前线程可以调用exit,然后在Debug调用之前,另一个线程可以Enter监视并将其写入“Entered”行。

如果您将代码更改为:

 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();
    }
}
于 2013-03-12T17:27:48.130 回答
4

编辑:放弃了所有这些。答案是:为什么我不能在 lock 语句的主体中使用 'await' 运算符?

你不应该做你想做的事情,在本质上是一个“lock”语句中使用“await”。

于 2013-03-12T17:37:01.380 回答
0

您不能Monitorasync方法中使用标准的多线程原语。如果您使用lock语句,C# 编译器会阻止您,但如果您通过“手动”执行它,Monitor.Enter那么您最终只会在脚下开枪。

如果你想一想,这完全有道理。当async方法产生时会发生什么?它返回一个未完成的Task允许其他代码在它(a)等待时运行。这完全违背了锁的观点,即防止其他代码在持有锁时运行。还有一些情况(例如 ASP.NET 或线程池上下文)async方法在不同的线程上恢复;在这种情况下,一个线程获取锁,另一个线程释放它。疯狂!猫和狗住在一起!

内置的 .NET 解决方案是SemaphoreSlim,它用作锁/信号量(尽管它没有内置IDisposable用于与 一起使用using)。asyncStephen Toub 写了一个关于协调原语的博客系列,我在我的 AsyncEx 库中实现了一个完整的套件,可通过 NuGet 获得。

于 2013-03-12T18:13:26.617 回答