哪个是异步方法返回任务的正确方法?

时间:2016-09-22 14:30:07

标签: c# async-await

我对以下方法感到困惑。哪一个是最好的,为什么?这些工作正常。

public string GetString(int i)
{
    return "Testing number " + i.ToString();
}

//async methods where I'm confused with
public Task<string> GetStringAsync(int i)
{
    return Task.FromResult<string>(GetString(i));
}
//Or
public Task<string> GetStringAsync(int i)
{
    Task<string> task = new Task<string>(() => GetString(i));
    task.Start();
    return task;
}
//Or
public Task<string> GetStringAsync(int i)
{
    var tcs = new TaskCompletionSource<string>();
    tcs.SetResult(GetString(i));
    return tcs.Task;
}

来电者将

Task<string> task = SomeClass.GetStringAsync(9);
Console.WriteLine(task.Result);
//Or
var result = await SomeClass.GetStringAsync(9);
Console.WriteLine(result);

非常感谢你。

2 个答案:

答案 0 :(得分:6)

我认为您可能无法完全理解为什么任何人都希望使用异步。 Async .Net允许释放通常等待某些外部操作的线程(网络呼叫或硬盘调用)。通常,Windows使用I/O Completion Ports进行这些调用,并且最近添加了async / await关键字以允许.Net访问本机异步调用。也就是说,使用异步时会有一点开销,因为运行时必须create a state-machine来跟踪线程当前状态,然后分配 await'd线程一个新任务。

因此,任何不使用I / O完成端口的异步任务最有可能弊大于利。

//async methods where I'm confused with
public Task<string> GetStringAsync(int i)
{
    return Task.FromResult<string>(GetString(i));
}

让我正确地将其编码为异步方法:

//async methods where I'm confused with
public async Task<string> GetStringAsync(int i)
{
    return await Task.FromResult<string>(GetString(i));
}

还不错,因为没有理由使用异步;除非我弄错Task.FromResult()如何运作,否则这会产生额外的开销,无益。

我只是将它们全部重写为异步,然后让我了解它们的影响。

public async Task<string> GetStringAsync(int i)
{
    Task<string> task = new Task<string>(() => GetString(i));
    await task.Start();
    return task;
}

您应该创建和开始自己的任务it's not a good idea极其罕见。

public Task<string> GetStringAsync(int i)
{
    var tcs = new TaskCompletionSource<string>();
    tcs.SetResult(GetString(i));
    return tcs.Task;
}

TaskCompletionSource主要针对wrap current asynchronous into the async/await pattern。由于这不包装任何类型的异步操作,因此没有性能优势。

public string GetString(int i)
{
    return "Testing number " + i.ToString();
}

这是你最好的选择。除非实际需要,否则不要使用async / await。

Stephen Cleary has a great set of Posts that talk about Tasks and Async,我强烈建议您在深入了解.Net中的Async之前先阅读它。

答案 1 :(得分:0)

实际上,以上都不正确。

public Task<string> GetStringAsync(int i)
{
    Task<string> task = new Task<string>(() => GetString(i));
    task.Start();
    return task;
}

实际应该是:

public async Task<string> GetStringAsync(int i)
    {
        return await Task.Run(() => GetString(i));
    }

特别注意,使用&#34; async&#34;关键字(允许使用&#34; await&#34;关键字;&#34; await&#34;相当于&#34;当我得到你的结果时回到我身边,&#34;有关此创建的状态机的详细信息,请参阅文档。您提供的示例反映了执行任务并行性的旧方法,这不再是最佳实践。

此时需要注意的一点是:确保GetString是一个CPU绑定任务的情况。有几种异步方法:在同一个线程中异步运行的方法(主要用于IO绑定任务),在线程池中运行的方法(主要用于CPU绑定任务),以及首先不应该是异步的(既不受CPU限制也不受IO限制)。

我对这个事实的标准说明如下:假设你去一家有10个人的餐馆。当服务员来的时候,他要求订购的第一个人还没有准备好;然而,其他9人是。因此,服务员要求其他9个人的订单,然后回到原来的家伙,希望他到那时准备订购。 (绝对不是这样,他们会让第二个服务员等待原来的人准备订购,这样做可能不会节省太多时间)。这就是async / await在许多情况下是如何工作的(例外,一些任务并行库调用,如Thread.Run(...),实际上正在其他线程上执行 - 在我们的插图中,引入了一个第二个服务员 - 所以请确保检查哪个文件是哪个。

因此,当您使用async / await时,请确保您知道某些内容是否受CPU限制(因此适合在单独的线程上执行),IO绑定(因此适合于异步执行)相同的线程),或者两者都没有(因此首先使得异步毫无意义)。