首页 > 解决方案 > SynchronizationLockException:无法等待此运行时的监视器。- Blazor wasm 中 TaskCompletionSource 的解决方法

问题描述

我有一个异步方法,它返回输入表单的输入用户值。只要用户没有提交输入,异步方法Task<String> Read()就应该等待。当用户提交输入表单时,该方法Task Execute(EditContext context)被触发。因此,只要未提交表单,我就使用TaskCompletionSource来阻止该方法(这适用于 wpf 应用程序,我做到了)。Read


public async Task<String> Read()
{
    StringReadTaskCompletionSource = new TaskCompletionSource<string>();
    return await StringReadTaskCompletionSource.Task;
}
protected Task Execute(EditContext context)
{
    //...
    StringReadTaskCompletionSource
        .SetResult((context?.Model as ConsoleInput as ConsoleInput).Text);
}

但是通过上面的代码,我得到:

暴击:Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] 未处理的异常渲染组件:无法等待此运行时的监视器。System.Threading.SynchronizationLockException:无法等待此运行时的监视器。在(包装器托管到本机) System.Threading.Monitor.Monitor_wait(object,int) 在 System.Threading.Monitor.ObjWait (System.Boolean exitContext, System.Int32 毫秒超时, System.Object obj) <0x2e64fc8 + 0x00046>在 System.Threading.Monitor.Wait 的 :0 (System.Object obj, System.Int32 毫秒超时, System.Boolean exitContext) <0x2e64ce8 + 0x00022> 在 System.Threading.Monitor.Wait 的 :0 (System.Object obj, System .Int32 毫秒超时)

它看起来像是 razor-wasm 对任务和线程的限制的结果。我从这里尝试了解决方法:https ://github.com/dotnet/aspnetcore/issues/14253#issuecomment-534118256

通过使用 Task.Yield 但不成功。知道如何解决这个问题吗?

[编辑:] 我认为对我来说主要结论是,使用 razor-wasm(由于一个线程限制)运行同步方法(Console.ReadLine())并等待用户输入而不阻塞整个应用程序是不可能的。看起来没有解决方法。唯一的方法是将所有这些同步调用替换为新的异步调用,例如Console.ReadLineAsync().

标签: c#multithreadingasync-awaitblazorblazor-webassembly

解决方案


我通过您提供的 github 链接检查了代码,并注意到您正在这样做:

_stringReaderRedirect = new StringReaderRedirect(Read);

Read有问题的功能在哪里。然后在里面StringReaderRedirect你有:

private readonly Func<Task<string>> _ReadRedirectFunc;
public StringReaderRedirect(Func<Task<string>> readredirect) : base("foo")
{
    _ReadRedirectFunc = readredirect;
}

然后你这样做:

public override string ReadLine()
{
    //return _ReadRedirectFunc?.Invoke();
    //return base.ReadLine();
    Task<string> task = _ReadRedirectFunc?.Invoke();

    return task?.GetAwaiter().GetResult();
}

所以你阻塞了异步调用,这是有问题的异常的来源。在像 Blazor WASM 这样的单线程环境中,这样做是一个主要的禁忌。如果您看到的异常没有被抛出,那么您将遇到死锁:唯一的线程(UI)被阻塞等待结果Read,而Read它本身取决于用户输入,需要哪个 UI 线程。blazor github repo 上有很多类似的问题,例如.

Read().GetAwaiter().GetResult()顺便说一句,如果您从 UI 线程中进行操作,那么 WPF 中也会发生同样的情况。不一样,因为在 WPF 的情况下,它只会死锁,但也“不起作用”。

所以一直异步,永远不要阻塞主线程,因为它是你唯一的线程。


推荐阅读