问题
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");
});
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!
回答1:
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.
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.
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.
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);
来源:https://stackoverflow.com/questions/18882048/task-is-being-scheduled-on-the-same-thread-as-the-callers