在 UI 线程上创建并启动任务
Creating and starting a task on the UI thread
当在工作线程上调用的方法需要在 UI 线程上 运行 代码并等待它完成后再做其他事情时,可以这样完成:
public int RunOnUi(Func<int> f)
{
int res = Application.Current.Dispatcher.Invoke(f);
return res;
}
但是如果我想用任务来做呢? RunOnUi 方法是否可以创建一个在 UI 和 return 上启动的任务,以便调用者(工作线程上的 运行s)可以等待它?符合以下签名的内容:public Task<int> StartOnUi(Func<int> f)
?
一种方法如下:
public Task<int> RunOnUi(Func<int> f)
{
var task = new Task<int>(f);
task.Start(_scheduler);
return task;
}
这里,假设_schduler
持有uiTaskScheduler
。但是我不太喜欢创建 "cold" 任务并使用 start 方法来 运行 它们。这是 "recommended" 方式还是有更优雅的方式?
只需使用 InvokeAsync
instead of Invoke
then return the Task<int>
inside the DispatcherOperation<int>
函数 returns.
//Coding conventions say async functions should end with the word Async.
public Task<int> RunOnUiAsync(Func<int> f)
{
var dispatcherOperation = Application.Current.Dispatcher.InvokeAsync(f);
return dispatcherOperation.Task;
}
如果您无法访问 .NET 4.5,情况会稍微复杂一些。您将需要使用 BeginInvoke
and a TaskCompletionSource
to wrap the DispaterOperation
那 BeginInvoke
returns
public Task<int> RunOnUi(Func<int> f)
{
var operation = Application.Current.Dispatcher.BeginInvoke(f);
var tcs = new TaskCompletionSource<int>();
operation.Aborted += (sender, args) => tcs.TrySetException(new SomeExecptionHere());
operation.Completed += (sender, args) => tcs.TrySetResult((int)operation.Result);
//The operation may have already finished and this check accounts for
//the race condition where neither of the events will ever be called
//because the events where raised before you subscribed.
var status = operation.Status;
if (status == DispatcherOperationStatus.Completed)
{
tcs.TrySetResult((int)operation.Result);
}
else if (status == DispatcherOperationStatus.Aborted)
{
tcs.TrySetException(new SomeExecptionHere());
}
return tcs.Task;
}
当在工作线程上调用的方法需要在 UI 线程上 运行 代码并等待它完成后再做其他事情时,可以这样完成:
public int RunOnUi(Func<int> f)
{
int res = Application.Current.Dispatcher.Invoke(f);
return res;
}
但是如果我想用任务来做呢? RunOnUi 方法是否可以创建一个在 UI 和 return 上启动的任务,以便调用者(工作线程上的 运行s)可以等待它?符合以下签名的内容:public Task<int> StartOnUi(Func<int> f)
?
一种方法如下:
public Task<int> RunOnUi(Func<int> f)
{
var task = new Task<int>(f);
task.Start(_scheduler);
return task;
}
这里,假设_schduler
持有uiTaskScheduler
。但是我不太喜欢创建 "cold" 任务并使用 start 方法来 运行 它们。这是 "recommended" 方式还是有更优雅的方式?
只需使用 InvokeAsync
instead of Invoke
then return the Task<int>
inside the DispatcherOperation<int>
函数 returns.
//Coding conventions say async functions should end with the word Async.
public Task<int> RunOnUiAsync(Func<int> f)
{
var dispatcherOperation = Application.Current.Dispatcher.InvokeAsync(f);
return dispatcherOperation.Task;
}
如果您无法访问 .NET 4.5,情况会稍微复杂一些。您将需要使用 BeginInvoke
and a TaskCompletionSource
to wrap the DispaterOperation
那 BeginInvoke
returns
public Task<int> RunOnUi(Func<int> f)
{
var operation = Application.Current.Dispatcher.BeginInvoke(f);
var tcs = new TaskCompletionSource<int>();
operation.Aborted += (sender, args) => tcs.TrySetException(new SomeExecptionHere());
operation.Completed += (sender, args) => tcs.TrySetResult((int)operation.Result);
//The operation may have already finished and this check accounts for
//the race condition where neither of the events will ever be called
//because the events where raised before you subscribed.
var status = operation.Status;
if (status == DispatcherOperationStatus.Completed)
{
tcs.TrySetResult((int)operation.Result);
}
else if (status == DispatcherOperationStatus.Aborted)
{
tcs.TrySetException(new SomeExecptionHere());
}
return tcs.Task;
}