2

在 WinRT 商店应用程序中,Monitor 似乎无法按预期工作。我有以下代码:

protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        var tasks = Enumerable.Range(0, 10).Select((i)=>new Task(DoWork)).ToArray();
        foreach (var task in tasks)
        {
            task.Start();
        }

        Task.WaitAll(tasks);
    }

    static object lockObject = new Object();//typeof(MainPage)

    protected async void DoWork()
    {
        bool taken =false;
        Monitor.Enter(lockObject, ref taken);
        Debug.WriteLine("In");
        await Task.Delay(1000);
        Debug.WriteLine("Out");
        if (taken) Monitor.Exit(lockObject);
    }

在输出窗口中,我看到:

In
In
In
In
In
In
In
Out
Out
Out
Out
Out
Out
Out
In
Out
A first chance exception of type 'System.Threading.SynchronizationLockException' occurred in App4.exe

这意味着 Monitor 没有锁定关键区域。有人知道我做错了什么吗?

4

2 回答 2

6

您正在有效地尝试使用:

lock (lockObject)
{
    await Task.Delay(1000);
}

...除了 C# 编译器不允许你这样做,因为它会被破坏。当您的await表达式完成时,您可以在不同的线程上 - 所以当您调用 时Monitor.Exit,您很可能不在同一个线程上,因为您获得了锁定......因此出现了异常。

我建议您更改日志记录以显示:

  • 当您调用时In,您在哪个线程上以及taken之后的值(您可能会看到某些任务没有成功占用监视器,因为另一个线程拥有它 - 但见下文)
  • 在你打电话之前Monitor.Exit,你在哪个线程上

目前尚不清楚您要达到什么目的,但Monitor在这里使用几乎可以肯定是错误的方法。

还要注意,因为多个任务都可以在同一个线程上执行(不是同时,但是你用 await “放弃”线程)并且因为监视器是可重入的(一个线程可以多次获取监视器)你可以以及查看获取监视器的多个任务。

重要的是您要了解为什么这不起作用 - 并且您要了解线程与任务不同。然后你可以尝试开始研究如何真正实现你想要的,这几乎肯定不是通过Monitor.

于 2013-03-26T09:41:10.247 回答
1

Monitor不适用于async方法。

如果您想要async兼容的互斥,请尝试我SemaphoreSlim.WaitAsyncAsyncEx 库中的协调原语之一

于 2013-03-26T11:56:39.100 回答