C# 在UI线程上创建和启动任务

C# 在UI线程上创建和启动任务,c#,wpf,task-parallel-library,task,C#,Wpf,Task Parallel Library,Task,当在工作线程上调用的方法需要在UI线程上运行代码并等待其完成后再执行其他操作时,可以这样做: public int RunOnUi(Func<int> f) { int res = Application.Current.Dispatcher.Invoke(f); return res; } public int rununui(函数f) { int res=Application.Current.Dispatcher.In

当在工作线程上调用的方法需要在UI线程上运行代码并等待其完成后再执行其他操作时,可以这样做:

    public int RunOnUi(Func<int> f)
    {
        int res = Application.Current.Dispatcher.Invoke(f);

        return res;
    }
public int rununui(函数f)
{
int res=Application.Current.Dispatcher.Invoke(f);
返回res;
}
但是如果我想用任务来做呢?RunOnUi方法是否有办法创建在UI上启动的任务并返回它,以便调用方(在工作线程上运行)可以等待它?符合以下签名的内容:
公共任务StartOnUi(Func f)

一种方法如下:

public Task<int> RunOnUi(Func<int> f)
{
    var task = new Task<int>(f);
    task.Start(_scheduler);

    return task;
}
public Task rununui(函数f)
{
var任务=新任务(f);
task.Start(_调度程序);
返回任务;
}
这里,假设
\u调度器
持有ui
任务调度器
。但是我不太习惯于创建“冷”任务并使用start方法来运行它们。这是“推荐”方法还是有更优雅的方法呢?

只需使用而不是
Invoke
,然后在函数返回的内部返回
任务

//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;
}

需要注意的是,
Dispatcher.InvokeAsync
仅在.NET 4.5及更高版本中受支持。@KevinD。公平地说,我也将使用4.5版本之前的解决方案进行更新。@KevinD。刚刚注意到OP必须在4.5上,因为
Dispatcher.Invoke(Func)
也是4.5版的唯一功能。我正在寻找.net 4.5的解决方案,谢谢你,这应该是一个你很少做的操作。如果你发现自己经常做这样的事情,这表明你的程序设计得不好。在UI环境中,您通常应该让大部分代码在UI线程中运行,并且只有非常本地化的部分,没有UI交互被卸载到其他地方,并且被等待。每当你觉得需要这样做时,通常都是一个迹象,表明你应该从操作中提取UI代码。你是对的。这是WPF应用程序作为WCF服务执行的特殊情况。每个服务调用都在工作线程上接收,该工作线程需要等待在UI上执行某些操作的任务(执行更改或读取其中的某些值)。
    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;
    }