SynchronizationLockException:无法在此运行时等待监视器。 - Blazor wasm 中 TaskCompletionSource 的解决方法
SynchronizationLockException: Cannot wait on monitors on this runtime. - Workaround for TaskCompletionSource in Blazor wasm
我有一个异步方法,它返回输入表单中输入的用户值。只要用户没有提交输入,异步方法 Task<String> Read()
就应该等待。当用户提交输入表单时,方法 Task Execute(EditContext context)
被触发。因此,只要表单未提交,我就使用 TaskCompletionSource
来阻止 Read
方法(这适用于 wpf 应用程序,我做到了)。
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);
}
但是通过上面的代码我得到:
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Cannot wait on monitors on this runtime.
System.Threading.SynchronizationLockException: Cannot wait on monitors on this runtime.
at (wrapper managed-to-native) System.Threading.Monitor.Monitor_wait(object,int)
at System.Threading.Monitor.ObjWait (System.Boolean exitContext, System.Int32 millisecondsTimeout, System.Object obj) <0x2e64fc8 + 0x00046> in :0
at System.Threading.Monitor.Wait (System.Object obj, System.Int32 millisecondsTimeout, System.Boolean exitContext) <0x2e64ce8 + 0x00022> in :0
at System.Threading.Monitor.Wait (System.Object obj, System.Int32 millisecondsTimeout)
这看起来像是 razor-wasm 在任务和线程方面的限制的结果。我从这里尝试了解决方法:https://github.com/dotnet/aspnetcore/issues/14253#issuecomment-534118256
通过使用 Task.Yield 但未成功。知道如何解决这个问题吗?
[编辑:]我认为我的主要结论是,razor-wasm(由于单线程限制)不可能成为 运行 一种同步方法(Console.ReadLine()
) ,并等待用户输入,而不会阻塞整个应用程序。看起来没有解决方法。唯一的方法是将所有这些同步调用替换为新的异步调用,如 Console.ReadLineAsync()
.
我检查了您提供的 github link 的代码,发现您正在这样做:
_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,for example.
上有很多类似的问题
顺便说一句,如果您从 UI 线程执行 Read().GetAwaiter().GetResult()
,WPF 中也会发生同样的情况。嗯,不一样,因为在 WPF 的情况下,它只会死锁,但也“不会工作”。
所以一路异步,永远不要阻塞主线程,因为它是你拥有的唯一线程。
我有一个异步方法,它返回输入表单中输入的用户值。只要用户没有提交输入,异步方法 Task<String> Read()
就应该等待。当用户提交输入表单时,方法 Task Execute(EditContext context)
被触发。因此,只要表单未提交,我就使用 TaskCompletionSource
来阻止 Read
方法(这适用于 wpf 应用程序,我做到了)。
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);
}
但是通过上面的代码我得到:
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Cannot wait on monitors on this runtime. System.Threading.SynchronizationLockException: Cannot wait on monitors on this runtime. at (wrapper managed-to-native) System.Threading.Monitor.Monitor_wait(object,int) at System.Threading.Monitor.ObjWait (System.Boolean exitContext, System.Int32 millisecondsTimeout, System.Object obj) <0x2e64fc8 + 0x00046> in :0 at System.Threading.Monitor.Wait (System.Object obj, System.Int32 millisecondsTimeout, System.Boolean exitContext) <0x2e64ce8 + 0x00022> in :0 at System.Threading.Monitor.Wait (System.Object obj, System.Int32 millisecondsTimeout)
这看起来像是 razor-wasm 在任务和线程方面的限制的结果。我从这里尝试了解决方法:https://github.com/dotnet/aspnetcore/issues/14253#issuecomment-534118256
通过使用 Task.Yield 但未成功。知道如何解决这个问题吗?
[编辑:]我认为我的主要结论是,razor-wasm(由于单线程限制)不可能成为 运行 一种同步方法(Console.ReadLine()
) ,并等待用户输入,而不会阻塞整个应用程序。看起来没有解决方法。唯一的方法是将所有这些同步调用替换为新的异步调用,如 Console.ReadLineAsync()
.
我检查了您提供的 github link 的代码,发现您正在这样做:
_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,for example.
顺便说一句,如果您从 UI 线程执行 Read().GetAwaiter().GetResult()
,WPF 中也会发生同样的情况。嗯,不一样,因为在 WPF 的情况下,它只会死锁,但也“不会工作”。
所以一路异步,永远不要阻塞主线程,因为它是你拥有的唯一线程。