【发布时间】:2022-01-28 08:10:57
【问题描述】:
我有 WinForms 应用程序,其中按钮单击调用外部库的一些异步方法。
private async void button1_Click(object sender, EventArgs e)
{
await CallLibraryAsync();
}
private static async Task CallLibraryAsync()
{
var library = new Library();
await library.DoSomethingAsync();
}
库如下所示:
public class Library
{
public async Task DoSomethingAsync()
{
Thread.Sleep(2000);
await Task.Delay(1000).ConfigureAwait(false);
// some other code
}
}
在任何异步代码之前,都有一些由Thread.Sleep 调用模拟的计算。在这种情况下,此调用将阻塞 UI 线程 2 秒。 我无法更改DoSomethingAsync 中的代码。
如果我想解决阻塞问题,我可以像这样调用Task.Run 中的库:
private static async Task CallLibraryAsync()
{
var library = new Library();
// added Task.Run
await Task.Run(() => library.DoSomethingAsync());
}
它解决了这个问题,UI 不再阻塞,但我从 ThreadPool 中消耗了一个线程。这不是很好的解决方案。
如果我想在没有其他线程的情况下解决这个问题,我可以这样做:
private static async Task CallLibraryAsync()
{
var library = new Library();
// added
await YieldOnlyAsync().ConfigureAwait(false);
await library.DoSomethingAsync();
}
// added
private static async Task YieldOnlyAsync()
{
await Task.Yield();
}
此解决方案有效。 Task.Yield() 导致该方法 YieldOnlyAsync() 始终异步运行,ConfigureAwait(false) 导致下一个代码 (await library.DoSomethingAsync();) 在某个 ThreadPool 线程上运行,而不是 UI 线程。
但这是一个相当复杂的解决方案。有没有更简单的?
编辑:
如果库方法看起来像这样
public class Library
{
public async Task DoSomethingAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
Thread.Sleep(2000);
await Task.Delay(1000);
// some other code
}
}
UI 线程不会被阻塞,我不需要做任何事情。但这就是我没有直接看到的一些实现细节的问题,因为这可能在一些 nuget 包中。当我看到 UI 在某些情况下冻结时,我可能会在一些调查后发现这个问题(意味着在异步方法中的任何等待之前的 CPU 绑定计算)。没有Wait()或Result,那会很容易找到,这个比较麻烦。
如果可能,我希望以更简单的方式为这种情况做好准备。这就是为什么我不想在调用某个第三方库时使用Task.Run。
【问题讨论】:
-
还有更简单的吗? - 如果你无法更改库,请将
DoSomethingAsync的代码复制到你自己的代码库中并注释掉 Sleep?或者如何编辑编译的库以删除调用? -
不要担心使用线程池中的线程,特别是桌面应用程序,因为您的情况就是这样。他们在那里的唯一目的是被使用。他们将在工作完成后立即退出。
-
所以库执行了一个很长的 CPU 绑定计算,在它等待之前。我有点不明白为什么不愿意 Task.Run 它;它必须在某个地方运行,那么你会在哪里运行呢?
-
我认为您是说您不一定知道某些外部库在做什么以及它是否是 CPU 密集型操作。但是你总是会知道它在做什么,否则你为什么要使用它呢?而且,你会在测试中发现:如果它锁定了 UI,那么使用
Task.Run。 -
@JeremyLakeman,不,
ConfigureAwait()是Task类的方法。YieldAwaitable(由Task.Yield()返回)没有那个方法。
标签: c# winforms async-await threadpool