我的应用程序有一个包含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
有什么关系吗?由于某种原因,调度程序决定将任务放在同一个线程中,但我不明白为什么。欢迎任何建议!
答案 0 :(得分:7)
StartNew
无法确保任务在新线程中运行。它使用TaskScheduler.Current
来安排新任务。在整个代码的许多地方,这将是null
。当它为null
时,将使用TaskScheduler.Default
,这将调度委托在线程池中运行。
在您的特定情况下,Current
不为空。它是某个任务调度程序的表示,它调度委托在UI线程中运行。
可能发生这种情况的一种方法是,您运行的代码是使用UI同步上下文调用StartNew
或ContinueWith
的结果。在任何一种情况下执行的委托期间,它会将当前的调度程序设置为基于所提供的SynchronizationContext
的调度程序,即UI上下文。
如果您使用Task.Run
,则可以避免此问题;它将始终使用默认任务计划程序而不是当前计划程序。
您的另一个选择是明确声明您需要默认任务计划程序:
Task.Factory.StartNew(() => { }
, CancellationToken.None
, TaskCreationOptions.None
, TaskScheduler.Default);