在WPF应用程序中实现Execute()
的类的ICommand
中,我对一个返回Task
的方法进行外部调用
ICommand类:
public static void Execute(object parameter)
{
Cancel(arg1, arg2);
}
private static void Cancel(IList<object> arg1, object arg2)
{
Task<object> cancelTask = service.AmendAsync
(
CancelTokenSource.Token,
object arg2
);
ProcessCancellingResponse(arg1, arg2);
}
private static void ProcessCancellingResponse(IList<object> arg1, Task<object> cancelTask)
{
cancelTask.ContinueWith
(
task =>
{
Update(task.Result.Response);
},
CancelTokenSource.Token,
TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext()
);
}
服务类:
public Task<object> AmendAsync(CancellationToken cancellationToken, object arg1)
{
return Task<object>.Factory.StartNew
(
() =>
{
...
},
cancellationToken,
TaskCreationOptions.None,
TaskScheduler.Default
);
}
我的问题是
cancelTask.ContinueWith()
等待UI线程或后台线程吗?即如果Task
需要很长时间并且在UI线程上等待,则UI可能会冻结。答案 0 :(得分:2)
根据Domysee评论,我会在这里清楚,Execute
总是在UI线程中运行,但你可以在回调中做任何你想做的事情,包括运行背景螺纹,
关于延续,如果你没有明确告诉他线程继续,它将在TaskScheduler.Current
上完成它的工作,否则它将继续你定义的调度程序。
无论如何,考虑使用async\await
和\ out捕获来进行延续
await Task.Run(() => ).ConfigureAwait(true);
await Task.Run(() => ).ConfigureAwait(false);
<强>更新强>
根据问题更新,
执行 - &gt; UI线程
取消=&gt; UI线程
AmendAsync =&gt;后台线程
ContinueWith =&gt; UI线程(因为你写了FromCurrentSynchronizationContext
)
答案 1 :(得分:2)
什么线程调用ICommend Execute()它是UI线程吗?
是的,它始终位于UI线程上。
cancelTask.ContinueWith()会在UI线程上还是在后台线程上等待?
ContinueWith
只是常规方法调用。没有魔力。所以打破它:
此:
private static void ProcessCancellingResponse(IList<object> arg1, Task<object> cancelTask)
{
cancelTask.ContinueWith
(
task =>
{
Update(task.Result.Response);
},
CancelTokenSource.Token,
TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext()
);
}
与此相同:
private static void ProcessCancellingResponse(IList<object> arg1, Task<object> cancelTask)
{
Action<Task> continuation = task => { Update(Task.Result.Response); };
var token = CancelTokenSource.Token;
var options = TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion;
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
cancelTask.ContinueWith(continuation, token, options, scheduler);
}
由于在UI线程上调用ProcessCancellingResponse
,因此scheduler
将是在该UI线程上执行其任务的调度程序。因此,continuation
将在该UI线程上运行。
另一方面,我看到至少有一个错误:AttachedToParent
几乎肯定是错的。承诺任务(异步任务)几乎不应该附加任务。
实施也可以更清洁:
private static async Task ProcessCancellingResponseAsync(IList<object> arg1, Task<object> cancelTask)
{
var result = await cancelTask;
Update(result.Response);
}
public object Amend(CancellationToken cancellationToken, object arg1)
{
...
}
private static void Cancel(IList<object> arg1, object arg2)
{
Task<object> cancelTask = Task.Run(() => service.Amend
(
CancelTokenSource.Token,
object arg2
);
ProcessCancellingResponse(arg1, arg2);
}
答案 2 :(得分:1)
直接从UI调用时,ICommand Execute()在主线程(UI线程)上运行。
这取决于代码的位置。如果它直接位于Execute中,它也将在主线程上运行,因为您将调度程序指定为TaskScheduler.FromCurrentSynchronizationContext()
。