问题描述
我的应用程序有一个视图模型,其中包含一个 Lazy
字段.该字段使用对服务器的服务调用来填充.在图像很大的情况下,服务器需要几秒钟才能返回图像(实际上是一个 byte[]
),因此 UI 被阻止.为了防止这种情况,我将服务调用放在 Task
中,以便后台线程获取图像,然后调用 OnPropertyChanged
以让 UI 知道图像已返回:
My application has a View Model which contains a Lazy<BitmapImage>
field. The field is populated using a service call to the server. In cases where the image is large, it takes a few seconds for the server to return the image (which is in fact a byte[]
) therefore the UI is blocked. To prevent this, I put the service call in a Task
, so that a background thread gets the image and then calls the OnPropertyChanged
to let the UI know the image is returned:
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
有什么关系吗?出于某种原因,调度程序决定将任务放在同一个线程中,但我不明白为什么.欢迎任何建议!
I noticed that even after putting the service call in a Task
, the UI is till blocked. So added those Console.WriteLine
lines to see the thread IDs. Surprisingly enough, both of them report the same thread ID (this seems to happen only in this case.I tried it with other tasks in the project, and they all report different IDs). Any idea what's going on here? Does it have anything to do with the BitmapImage
? For some reason the scheduler decides to put the task in the same thread, but I don't understand why. Any suggestions are welcome!
推荐答案
StartNew
不确保任务在新线程中运行.它使用 TaskScheduler.Current
来安排新任务.在整个代码的许多地方,这将是 null
.当它为null
时,则使用TaskScheduler.Default
,这将调度委托在线程池中运行.
StartNew
doesn't ensure that the task is run in a new thread. It uses TaskScheduler.Current
to schedule the new task. In many places throughout your code this will be null
. When it is null
, then TaskScheduler.Default
will be used, which will schedule the delegate to run in the thread pool.
在您的特定情况下 Current
不为空.它是一些任务调度器的表示,它调度委托在 UI 线程中运行.
In your particular case Current
is not null. It is the representation of some task scheduler that schedules the delegates to run in the UI thread.
发生这种情况的一种方式是,如果您正在运行的代码是使用 UI 同步上下文调用 StartNew
或 ContinueWith
的结果.在任一情况下执行的委托期间,它会将当前调度程序设置为基于提供的 SynchronizationContext
的调度程序,即 UI 上下文.
One way this may have happened is if the code that you are running is the result of a call to StartNew
or ContinueWith
with the UI synchronization context. During the delegates executed in either case it will set the current scheduler to be one that is based on the SynchronizationContext
provided, namely the UI context.
如果你使用 Task.Run
你就可以避免这个问题;它将始终使用默认的任务调度程序而不是当前的调度程序.
If you use Task.Run
you avoid the issue; it will always use the default task scheduler instead of the current one.
您的另一个选择是明确声明您想要默认任务调度程序:
Your other option is to explicitly state you want the default task scheduler:
Task.Factory.StartNew(() => { }
, CancellationToken.None
, TaskCreationOptions.None
, TaskScheduler.Default);
这篇关于任务被安排在与调用者相同的线程上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!