c# - 编写自己的异步方法

标签 c# .net asynchronous task-parallel-library async-await

我想知道如何以“正确”的方式编写您自己的异步方法。

我看过很多这样解释异步/等待模式的帖子:

http://msdn.microsoft.com/en-us/library/hh191443.aspx

// Three things to note in the signature: 
//  - The method has an async modifier.  
//  - The return type is Task or Task<T>. (See "Return Types" section.)
//    Here, it is Task<int> because the return statement returns an integer. 
//  - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the 
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();

    // The await operator suspends AccessTheWebAsync. 
    //  - AccessTheWebAsync can't continue until getStringTask is complete. 
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync. 
    //  - Control resumes here when getStringTask is complete.  
    //  - The await operator then retrieves the string result from getStringTask. 
    string urlContents = await getStringTask;

    // The return statement specifies an integer result. 
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 
    return urlContents.Length;
}

private void DoIndependentWork()
{
    resultsTextBox.Text += "Working........\r\n";
}

这适用于任何已经实现此功能的 .NET 方法,例如

  • System.IO 操作
  • 数据库操作
  • 网络相关操作(下载、上传...)

但是,如果我想编写自己的方法,但需要相当长的时间才能完成,而我无法使用任何方法,而且重负载在上面示例的 DoIndependentWork 方法中,该怎么办?

在这种方法中我可以做到:

  • 字符串操作
  • 计算
  • 处理我自己的元素
  • 聚合、比较、过滤、分组、处理内容
  • 列表操作、添加、删除、复制

我又一次偶然发现了很多帖子,人们只是在做以下事情(再次以上面的例子为例):

async Task<int> AccessTheWebAsync()
{ 
    HttpClient client = new HttpClient();

    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    await DoIndependentWork();

    string urlContents = await getStringTask;

    return urlContents.Length;
}

private Task DoIndependentWork()
{
    return Task.Run(() => {

        //String manipulations
        //Calculations
        //Handling my own objects
        //Aggregating, comparing, filtering, grouping, handling stuff
        //List operations, adding, removing, coping
    });
}

您可能会注意到变化是 DoIndependentWork 现在返回一个 Task 并且在 AccessTheWebAsync 任务中该方法得到一个 await

重负载操作现在封装在 Task.Run() 中,这就是全部吗? 如果仅此而已,我唯一需要做的就是为我的库中的每个方法提供异步方法,如下所示:

public class FooMagic
{
    public void DoSomeMagic()
    {
        //Do some synchron magic...
    }

    public Task DoSomeMagicAsync()
    {
        //Do some async magic... ?!?
        return Task.Run(() => { DoSomeMagic(); });
    }
}

如果你能向我解释一下就好了,因为即使是像这样的高票问题: How to write simple async method?只用现有的方法解释它,并且只使用 asyn/await 模式,就像上面提到的问题的评论一样,直截了当: How to write simple async method?

最佳答案

实际答案

您可以使用 TaskCompletionSource 来做到这一点,它有一个 Promise Task它不执行任何代码,只执行:

"Represents the producer side of a Task unbound to a delegate, providing access to the consumer side through the Task property."

当您开始异步操作时,您将该任务返回给调用者,并在您结束它时设置结果(或异常/取消)。确保操作真的是异步的是你的责任。

这是 Stephen Toub's AsyncManualResetEvent 中这种 root of all async 方法的一个很好的例子实现:

class AsyncManualResetEvent 
{ 
    private volatile TaskCompletionSource<bool> _tcs = new TaskCompletionSource<bool>();

    public Task WaitAsync() { return _tcs.Task; } 
    public void Set() { _tcs.TrySetResult(true); } 
    public void Reset() 
    { 
        while (true) 
        { 
            var tcs = _tcs; 
            if (!tcs.Task.IsCompleted || 
                Interlocked.CompareExchange(ref _tcs, new TaskCompletionSource<bool>(), tcs) == tcs) 
                return; 
        } 
    } 
}

背景

使用 async-await 基本上有两个原因:

  1. 改进的可伸缩性:当您有I/O 密集型工作(或其他固有的异步操作)时,您可以异步调用它,因此您释放调用线程,它是能够同时做其他工作。
  2. 卸载:当您有 CPU 密集型工作时,您可以异步调用它,这会将一个线程的工作转移到另一个线程(主要用于 GUI 线程)。

因此,大多数 .Net 框架的异步调用都支持开箱即用的 async 并且为了卸载,您可以使用 Task.Run(如你的例子)。您真正需要自己实现async的唯一情况是当您创建一个新的异步调用(I/O 或 async synchronization constructs for例子)。

这些情况极为罕见,这就是为什么您大多会找到以下答案的原因

"Only explains it with already existing methods and just using async/await pattern"


您可以在 The Nature of TaskCompletionSource 中更深入地了解

关于c# - 编写自己的异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24953808/

相关文章:

c# - 等待 Observable 的第 n 个 OnNext()

nhibernate - 如何在 Web API 异步任务中保留 HttpContext

c# - 所有情况下的 IoC 容器? (针对静态类)

ASP.NET MVC 条件 ViewModel 抽象

c# - 使用 lambda 初始化类型数组

.net - 是否有一个 .NET 类可以从 LDAP 中解析 CN= 字符串?

.net - 在 String.Format() 中转义单引号

javascript - 对于 NodeJs Promise,Await 和 Then 哪个更好?以及等待如何工作

c# - WCF 通话 - 最佳实践

c# - 在 C# 中进行 Windows Phone 开发的 Intent