任务正在与调用方的线程相同的线程上调度

本文关键字:线程 调度 调用 任务 | 更新日期: 2023-09-27 18:32:04

我的应用程序有一个视图模型,其中包含一个Lazy<BitmapImage>字段。该字段使用对服务器的服务调用进行填充。在图像很大的情况下,服务器需要几秒钟才能返回图像(实际上是byte[]),因此UI被阻止。为了防止这种情况,我将服务调用放在Task中,以便后台线程获取图像,然后调用OnPropertyChanged让 UI 知道返回了图像:

Console.WriteLine("Outside Task ThreadID: {0}",
    Thread.CurrentThread.ManagedThreadId);
Task.Factory.StartNew(() =>
{
    Console.WriteLine("Inside Task ThreadID: {0}", Thread.CurrentThread.ManagedThreadId);
    return Utilities.ConvertByteToImage(
              SessionService.GetUserInformation(UserInfo.From).ProfilePicture);
}).ContinueWith(resultToken =>
{
    m_lazyProfilePicture = new Lazy<BitmapImage>(() =>
    {
        return (resultToken.Result == null) ? Utilities.DefaultProfilePicture.Value : resultToken.Result;
    });
    OnPropertyChanged("ProfilePicture");
});

我注意到,即使在将服务调用放入Task后,UI 也会被阻止。因此,添加了这些Console.WriteLine行以查看线程 ID。令人惊讶的是,它们都报告相同的线程 ID(这似乎只在这种情况下发生。我尝试了项目中的其他任务,它们都报告了不同的ID)。知道这里发生了什么吗?它与BitmapImage有什么关系吗?出于某种原因,调度程序决定将任务放在同一个线程中,但我不明白为什么。欢迎任何建议!

任务正在与调用方的线程相同的线程上调度

StartNew不能

确保任务在新线程中运行。 它使用TaskScheduler.Current来计划新任务。 在整个代码中的许多地方,这将null。 当它被null时,将使用TaskScheduler.Default,这将调度委托在线程池中运行。

在您的特定情况下,Current不为空。 它是某些任务计划程序的表示形式,用于计划委托在 UI 线程中运行。

发生这种情况

的一种方式是,您正在运行的代码是调用 UI 同步上下文StartNewContinueWith的结果。 在任一情况下执行的委托期间,它都会将当前调度程序设置为基于提供的SynchronizationContext,即 UI 上下文。

如果使用Task.Run则可以避免此问题;它将始终使用默认任务计划程序而不是当前任务计划程序。

您的另一个选项是明确声明您需要默认任务计划程序:

Task.Factory.StartNew(() => { }
    , CancellationToken.None
    , TaskCreationOptions.None
    , TaskScheduler.Default);