正在与调用者的同一个线程上安排任务

Ari*_*edi 0 c# scheduler task-parallel-library

我的应用程序有一个包含Lazy<BitmapImage>字段的View Model .使用对服务器的服务调用填充该字段.在图像很大的情况下,服务器返回图像(实际上是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");
});
Run Code Online (Sandbox Code Playgroud)

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

Ser*_*rvy 7

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

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

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

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

您的另一个选择是明确声明您需要默认任务调度程序:

Task.Factory.StartNew(() => { }
    , CancellationToken.None
    , TaskCreationOptions.None
    , TaskScheduler.Default);
Run Code Online (Sandbox Code Playgroud)