使方法异步

本文关键字:异步 方法 | 更新日期: 2023-09-27 18:08:02

我有一个调用Bing Api的方法。

IEnumerable<WebResult> Search(string query)

我想让它异步,这样如果我对它进行多次调用,每个调用都是独立的。因此,按照这里的建议,我将签名更改为

async Task<IEnumerable<WebResult>> SearchAsynch(string query)

但是我得到了警告

This async method lacks 'await' operators and will run synchronously...

我希望整个方法是异步的(至少这是我认为它应该如何工作)。我怎么做呢?这是我的代码

public async Task<IEnumerable<WebResult>> SearchAsynch(string query)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query cannot be null");
            }
            DataServiceQuery<WebResult> webQuery = _bingContainer.Web(query, null, null, null, null, null, null, null);
            IEnumerable<WebResult> webResults = webQuery.Execute();
            return webResults;
        }

问题是我不确定在这段代码中等待什么

使方法异步

async关键字不创建一个新的异步操作,它只是一种更容易地为已经存在的任务(即异步操作)配置延续的方法。

在本例中,DataServiceQuery类已经提供了异步执行查询的方法。不幸的是,它使用的是旧的异步模型,而不是新的基于Task的模型,所以你需要使用Task.Factory.FromAsync来翻译它:

public Task<IEnumerable<WebResult>> SearchAsynch(string query)
{
    DataServiceQuery<WebResult> webQuery = _bingContainer.Web(query, null, null, null, null, null, null, null);
    return Task.Factory.FromAsync(webQuery.BeginExecute(null, null)
        , asyncResult => webQuery.EndExecute(asyncResult)));
}

在这种特殊情况下,由于您不需要做任何事情,除了创建任务,您根本不需要asyncawait,您可以只返回已构建的任务。如果你想在你得到结果后做一些事情,你可以代替await任务:

public async Task<IEnumerable<WebResult>> SearchAsynch(string query)
{
    DataServiceQuery<WebResult> webQuery = _bingContainer.Web(query, null, null, null, null, null, null, null);
    var results = await Task.Factory.FromAsync(webQuery.BeginExecute(null, null)
            , asyncResult=> webQuery.EndExecute(asyncResult));
    Console.WriteLine("Hi there");
    return results;
}

我不太熟悉Bing API,但如果API不包含异步方法,您可以通过将调用包装在启动的Task中来实现异步。async修饰符不会使你的方法自动异步,它只允许在内部await其他异步方法。

所以,在你的情况下,这可能是最简单的:

public Task<IEnumerable<WebResult>> SearchAsync(string query)
    {
        if (query == null)
        {
            throw new ArgumentNullException("query cannot be null");
        }
        return Task.Run(() => 
            {
                DataServiceQuery<WebResult> webQuery = _bingContainer.Web(query, null, null, null, null, null, null, null);
                return webQuery.Execute();
            }
    }

然后你可以把这个方法await标记为其他方法async:

var result = await SearchAsync("yourQuery");

如果Bing API有async方法对Begin/End,你可以使用Task.Factory.FromAsync从async方法对创建任务。Servy的回答更详细。

按MSDN

通常,由async关键字修改的方法至少包含一个等待表达式或声明。该方法同步运行,直到它到达第一个await表达式。此时,它被挂起,直到等待的任务完成。与此同时,控件返回给该方法的调用者。如果该方法不包含await表达式或语句,然后同步执行。编译器警告提醒您任何不包含await的异步方法,因为这种情况可能表示错误。有关详细信息,请参见编译器警告(级别1)CS4014。

编辑:我想我应该补充这一点,但它已经在这篇文章的评论部分了。标记为async的方法在遇到await关键字之前实际上不会运行异步。

这是一个在独立项目中使用async的例子。

private async void button2_Click(object sender, EventArgs e)
{
    DataTable dtMessages = await getMessages2(string sqlConn2, nMessage);
}
public async Task<DataTable> getMessages2(string sqlConn, int n)
{
    //create your query and command
    var o = await cmd2.ExecuteReaderAsync();
    DataTable dtMessages = o;
    return dtMessages;
}

这个警告只是告诉你,无论你在哪里调用SearchAsynch,它都需要在它之前有await。

var result = await SearchAsynch(query);

然而,SearchAsynch并没有像其他人所说的那样正确实现。