正在等待C#中的后台线程

本文关键字:后台 线程 在等待 | 更新日期: 2023-09-27 18:22:16

之间有什么区别

public class Worker
{
    public static void Execute()
    {
        ExecuteInBackgroundThread().Wait();
    }
    private static Task ExecuteInBackgroundThread()
    {
        return Task.Run(() =>
        {
            // do some long running operation
        });
    }
}

public class Worker
{
    public static void Execute()
    {
        ExecuteInBackgroundThread().Wait();
    }
    private static async Task ExecuteInBackgroundThread()
    {
        await Task.Run(() =>
        {
            // do some long running operation
        });
    }
}

我注意到,从我的Windows Phone应用程序的UI线程调用Worker.Execute()的第二个版本时出现了问题。

相反,使用第一个版本似乎一切都很好。

ExecuteInBackgroundThread的第二个版本是否真的返回了一个可以等待的Task<T>?但如果不是这样的话,编译器是否应该给出一个错误,说我们没有返回任何东西?

正在等待C#中的后台线程

首先,应用程序被卡住的真正原因是死锁。这是因为您使用的是Task.Wait,它正在阻塞等待UI线程完成的任务上的UI线程。

之所以会发生这种情况,是因为只有一个UI线程,所以有一个SynchronizationContext只使用该特定线程。

您可以使用ConfigureAwait(false)来修复死锁,它不使用SynchronizationContext:

private static async Task<T> ExecuteInBackgroundThread<T>()
{
    await Task.Run(() =>
    {
        // do some long running operation
    }).ConfigureAwait(false);
}

除此之外,只要ExecuteInBackgroundThread中不再有对Task.Run调用之外的代码,这两个选项就基本相同。第一个选项稍微快一点,因为您使用的是task中的任务。直接运行。第二个选项在上面添加了另一个冗余异步层(包括状态机等)。

结论:你应该使用第一个选项。但是不要使用Wait,它不仅会阻塞线程,而且在某些情况下可能会导致死锁。

让我们看看对每个版本调用Execute的作用:

第一个版本调用CCD_ 12,CCD_。然后Execute阻塞,直到返回的任务完成。

第二个版本调用ExecuteInBackgroundThread,它创建一个Task,并调度一个将在任务完成时执行的延续,并返回另一个任务。然后CCD_ 17阻塞直到返回的任务完成。

不同之处在于,由于await关键字,第二个版本安排了一个延续。它的效果取决于调用Execute的线程的同步上下文。当您在UI线程上时,第二个版本将死锁,因为继续操作是在由于调用Wait而被阻止的UI线程上安排的。

如果使用控制台应用程序,则不会出现死锁,因为默认同步上下文使用线程池来执行延续。