我可以使用async和Task.Run来使复杂方法异步吗?

时间:2017-04-12 01:54:06

标签: c# asp.net async-await task

我有一个复杂的方法,这个方法可以创建webRequest,访问数据库和其他工作。

public string MyComplexMethod(params){
    //Access db
    //Make WebRequests
    //...
}

我以这种方式使这个方法异步:

public async Task<string> MyComplexMethod(params)
{
    //Access db
    //Make WebRequests
    //...
}

public async Task MyComplexMethodAsync(params)
{
    await Task.Run(() => MyComplexMethod()).ConfigureAwait(false);
}

我用这种方式调用我的复杂方法:

public void OtherMethod()
{
    //other stuff
    MyComplexMethodAsync(params);
    //other stuff
}

我可以使用async和主要的Task.Run这样复杂的方法吗? 或者有另一种方法使这个方法异步?

2 个答案:

答案 0 :(得分:2)

要被Task.Run方法包装,不需要是异步的

public string MyComplexMethod() {}

public Task MyComplexMethodAsync() 
{
    return Task.Run(() => MyComplexMethod());
}

但是使用上方的示例不会使async-await工作的方式变为异步
您提到您的复杂方法使用Web服务和数据库查询 - 这使您的方法成为使其异步的完美候选者 async-await主要是为了有效地处理外部资源而不使用额外的线程进行操作,只等待响应并且什么都不做。

只有您需要,它为每个“触及”外部资源的操作创建自己的异步方法,大多数“客户端”已经提供了使用数据库或Web服务的异步方法。

// Access Db
public async Task<object> GetValueFromDatabaseAsync()
{
    using (var connection = new SqlConnection(connectionString))
    using (var command = new SqlCommand(query, connection))
    {
        await connection.OpenAsync();
        return await command.ExecuteScalarAsync();
    }
}

// Make web request
public async Task<HttpResponseMessage> SendRequestToWebServiceAsync(SomeData data)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(webserviceUrl);
        return await client.PostAsJsonAsync("api/values", data);
    }
} 

然后你最终得到异步复杂方法

public async Task<string> MyComplexMethodAsync() 
{
    var value = await GetValueFromDatabaseAsync();
    var data = new SomeData(value);
    var response = await SendRequestToWebServiceAsync(data);

    return response.StatusCode.ToString();
}

异步方法的有趣部分,在您开始在应用程序中使用async-await方法后,它们开始在整个应用程序中传播,如僵尸:)

使用MyComplexMethodAsync yuo还需要将OtherMethod更改为异步

public async Task OtherMethodAsync()
{
    //other stuff
    await MyComplexMethodAsync(params);
    //other stuff
}

答案 1 :(得分:1)

我认为你在async / await的正确道路上。

async 告诉编译器您将在该方法中包装所有内容 ContinueWith 在您到达 await 关键字后。

public void Something()
{
     var task = new Task(() => DoWork());
     task.ContinueWith(() => MoreWorkAfter());
     task.Start();
}

//Is the same as

public async void Something()
{
     var task = new Task(() => DoWork());
     task.Start();
     await Task;
     MoreWorkAfter();
}

//Is also the same as 

public async void Something()
{
     var task = Task.Run(() => DoWork());
     await Task;
     MoreWorkAfter();
}

//Still the same as 

public async void Something()
{
     await Task.Run(() => DoWork());
     MoreWorkAfter();
}

要使方法本身等待,它还必须返回一个Task对象,因为Task具有await正在查找的GetAwaiter()。请记住,如果你没有开始任务,它将永远等待。所以这里的方法与其他人一样以等待的方式编写。

public Task SomethingAsync()
{
     return Task.Run(() => DoWork());
}

//Same as...

public async Task SomethingAsync()
{
     await Task.Run(() => DoWork());
}

//And now in other methods you can....

public async void AnotherMethod()
{
     await SomethingAsync();
     //Do more work after it's complete.
}

要取消的是,一旦它启动,任务就在一个新线程上运行并回答你的问题,是的,它会卸载工作。因此,对于您的原始方法保持相同。异步方法可以像你一样运行任务。

public string MyComplexMethod(params)
{
        //Access db
        //Make WebRequests
        //...
}

public async Task MyComplexMethodAsync(params)
{
    await Task.Run(() => MyComplexMethod()).ConfigureAwait(false);
}

但要注意的事情;是的,虽然你可以打电话:

await MyComplexMethodAsync(params);

您有ConfigureAwait(false);

这意味着在该方法中任务之后的任何代码都不会出现在当前上下文中。

public async Task MyComplexMethodAsync(params)
{
    //Main thread work here
    await Task.Run(() => MyComplexMethod()).ConfigureAwait(false);//Background work here
    //This will also be background work since CongfigureAwait(false);
}

public async Task MyComplexMethodAsync(params)
{
    //Main thread work here
    await Task.Run(() => MyComplexMethod()); //Background work here
    //Main thread work here again since CongfigureAwait defaults to true;
}

我说主线程工作,但假设主线程名为MyComplexMethodAsync。

掌握它的最简单方法就是想象一下,await关键字之后的所有内容基本上都是一个在任务完成时被调用的新Action;就像使用ContinueWith一样。

如果在方法中有await关键字之后有代码,那么它将被调度为在主线程上运行(考虑主线程称为开始的方法)。这意味着它有点像使用Dispatcher.BeginInvoke,如果你曾经使用它。其余部分排队等待以更好的方式在主线程上运行。 (再说一次,除非你把ConfigureAwait(false)放在那里然后它没有&#39;