1

我的应用程序有一个包含Lazy<BitmapImage>字段的视图模型。使用对服务器的服务调用填充该字段。在图像很大的情况下,服务器需要几秒钟才能返回图像(实际上是 a byte[]),因此 UI 被阻止。为了防止这种情况,我将服务调用放在 a 中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");
});

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

4

1 回答 1

8

StartNew不能确保任务在新线程中运行。它用于TaskScheduler.Current安排新任务。在整个代码中的许多地方,这将是null. 当它是null, thenTaskScheduler.Default将被使用,它将调度委托在线程池中运行。

在您的特定情况下Current不为空。它是一些任务调度程序的表示,它调度委托在 UI 线程中运行。

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

如果你使用Task.Run你会避免这个问题;它将始终使用默认的任务调度程序而不是当前的。

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

Task.Factory.StartNew(() => { }
    , CancellationToken.None
    , TaskCreationOptions.None
    , TaskScheduler.Default);
于 2013-09-18T21:02:50.853 回答