使用.NET 4.0和C# 4.0的任务异步模式(TAP)

9

有没有一种方法可以在.NET 4.0中不使用awaitasync关键字来使用Task-based Asynchronous Pattern (TAP)?(我之所以问是因为我们被困在C#4.0中。)

另外,有没有一个在.NET 4.0中使用TAP与WCF的示例?


1
为了确保您不会错过任何内容,也不会混淆 .net 和 c# 版本号:您可以将使用 await/async 的 C#5 代码编译为 .net 4.0 程序集。您只需要引用异步目标包(它在 nuget 上)即可使其正常运行。如果由于某种原因您被困在 C#4 编译器中,请参阅您的问题的答案,答案是肯定的。 - jods
5个回答

10
你可以这样做。TAP只是异步代码最佳实践的合理形式化。基本上,TAP方法:
  1. 返回一个“热”(正在运行的)TaskTask<TResult>
  2. 有一个“Async”后缀(例如SomeMethodAsync(...))。
  3. 被重载以接受取消标记。
  4. 快速返回给调用者(具有小的初始化阶段)。
  5. 如果I/O绑定,则不会占用线程。
因此,一个示例可能是:
private Task SomeMethodAsync(CancellationToken token)
{
    return Task.Factory.StartNew(() => 
    {
        // Do something...
    }, token);
}

使用该方法并附加继续使用UI同步上下文。

private void SomeOtherMethod()
{
    var tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext()

    // Options 1.
    Task t = SomeMethodAsync(token);
    Task c = t.ContinueWith(ant => 
        { 
            /* Do something and check exceptions */
        }, CancellationToken.None,
           TaskContinuationOptions.None, 
           scheduler);

    // Options 2.
    SomeMethodAsync(token).ContinueWith(ant => 
        { 
            /* Do something and check exceptions */
        }, CancellationToken.None,
           TaskContinuationOptions.None, 
           scheduler);
}

或者一次性完成所有操作

Task t = Task.Factory.StartNew(() => 
    {
        /* Do Something */
    }, token).ContinueWith(ant => 
        { 
            /* Do something on UI thread after task has completed and check exceptions */
        }, CancellationToken.None,
           TaskContinuationOptions.None, 
           scheduler);

您还可以手动实现TAP模式,以更好地控制实现。要手动实现TAP,请创建一个TaskCompletionSource<TResult>对象,执行异步操作,并在其完成时调用SetResultSetExceptionSetCanceled方法,或这些方法中的Try版本之一。当您手动实现TAP方法时,必须在表示异步操作完成时完成生成的任务。例如:

public static Task<int> ReadTask(this Stream stream, 
    byte[] buffer, int offset, int count, object state)
{
    var tcs = new TaskCompletionSource<int>();
    stream.BeginRead(buffer, offset, count, ar =>
    {
        try { tcs.SetResult(stream.EndRead(ar)); }
        catch (Exception exc) { tcs.SetException(exc); }
    }, state);
    return tcs.Task;
}

从使用者处消费TaskCompleationSource

希望这可以帮助您。


8
你不需要使用async/await关键字,可以使用ContinueWithTaskScheduler.FromCurrentSynchronizationContext
Task.Factory.StartNew(() => { return "test"; } )
            .ContinueWith(task => { this.textBox1.Text = task.Result; },
                            CancellationToken.None,
                            TaskContinuationOptions.None, 
                            TaskScheduler.FromCurrentSynchronizationContext());

这大致相当于
var str = await SomeFuncAsync() //returning "test";
this.textBox1.Text = str;

5

有没有一种方法可以在.NET 4.0中使用基于任务的异步模式(TAP)而不使用关键字await或async?我问这个问题是因为我们被困在C#4中。

一个值得注意和方便的技巧是使用yieldIEnumerable<Task>。例如,来自Stephen Toub的"使用任务处理异步操作序列"

IEnumerable<Task> DoExample(string input) 
{ 
    var aResult = DoAAsync(input); 
    yield return aResult; 
    var bResult = DoBAsync(aResult.Result); 
    yield return bResult; 
    var cResult = DoCAsync(bResult.Result); 
    yield return cResult; 
    … 
}

… 
Task t = Iterate(DoExample(“42”));

这样,您就可以拥有类似于async/await的伪同步线性代码流。

2
这是一种非常好的做事方式,不需要太多离开“async/await”思维方式(稍微不同的异常处理除外-您不能在“try-catch”块的主体中使用“yield”)。在简单情况下,连续和连续链没有问题,但是当您需要丰富的异步任务组合并且无法使用“async/await”时,这绝对是正确的方法。+1 - Kirill Shlenskiy

0

是的,有的。我想到了针对“Begin/End”方法的异步包装器;

public static Task<WebResponse> GetResponseAsync(this WebRequest client)
{
return Task<WebResponse>.Factory.FromAsync(client.BeginGetResponse,
client.EndGetResponse, null);
}

我猜你想要的是 使用Task实现WCF异步服务操作


非常感谢@serbasi分享这篇好文章。 - Frank Myat Thu

0

请说明下投票反对的原因。如果我的回答有误,我完全可以删除它。 - Adil
1
我已经取消了踩的操作。现在答案是正确的。如果您使用TPL手动实现TAP,那么您必须通过“Continuations”完成生成的任务,这就是所有等待操作所做的事情... - MoonKnight
@Killercam,是的,我明白了,它有点误导人...已更新答案。 - Adil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接