监视类与WinRT的意外行为

本文关键字:意外 WinRT 监视 | 更新日期: 2023-09-27 18:05:49

似乎Monitor在WinRT商店应用程序中没有像预期的那样工作。我有以下代码:

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没有锁定临界区域。有人知道我哪里做错了吗?

监视类与WinRT的意外行为

你正在有效地尝试使用:

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

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

我建议你改变你的日志显示:

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

不清楚你想要实现什么,但在这里使用Monitor几乎肯定是错误的方法。

还要注意,因为多个任务都可以在同一个线程上执行(不是同时执行,而是使用await"放弃"线程),并且因为监视器是可重入的(一个线程可以多次获取监视器),您可能会看到多个任务获取监视器。

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

Monitor不支持async方法。

如果你想要兼容async互斥,尝试SemaphoreSlim.WaitAsync或我的AsyncEx库中的一个协调原语。