这是异步运行吗?

时间:2015-01-24 04:31:45

标签: c# multithreading asynchronous task-parallel-library async-await

我目前有以下内容:

var tasks = new List<Task>();

foreach (myObject obj in myObjectList)
{
    tasks.Add(downloadBitmap(obj.profilePath, obj.id));
}

await Task.WhenAll(tasks);

downloadBitmap

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
RequestState myRequestState = new RequestState();
myRequestState.request = request;

// Start the asynchronous request.
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(RespCallback), Tuple.Create(myRequestState, actorID));

// this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, DefaultTimeout, true);

// The response came in the allowed time. The work processing will happen in the  
// callback function.
allDone.WaitOne();

RespCallBack

        Tuple<RequestState, int> state = (Tuple<RequestState, int>)asynchronousResult.AsyncState;
        RequestState myRequestState = state.Item1;
        int actorID = state.Item2;

        try
        {
            HttpWebRequest myHttpWebRequest = myRequestState.request;
            myRequestState.response = (HttpWebResponse)myHttpWebRequest.EndGetResponse(asynchronousResult);

            // Read the response into a Stream object.
            Stream responseStream = myRequestState.response.GetResponseStream();
            myRequestState.streamResponse = responseStream;
            Bitmap bitmap = new Bitmap(responseStream);

            // Do some work here

        }
        catch (WebException e)
        {
            Console.WriteLine("\nRespCallback Exception raised!");
            Console.WriteLine("\nMessage:{0}", e.Message);
            Console.WriteLine("\nStatus:{0}", e.Status);
        }
        finally
        {
            // Release the HttpWebResponse resource.
            myRequestState.response.Close();
        }
        allDone.Set();

我从MSDN网站获得了大部分内容。我也收到了警告:

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

表示DownloadBitmap功能。

我知道我在这个函数中没有使用await,但我之所以认为没有必要,因为BeginGetResponse已经asynchronous了?

不确定我对此的理解是否完全正确......

3 个答案:

答案 0 :(得分:1)

BeginGetResponse是异步的,但它在.NET中使用较旧的异步编程范例,称为Asynchronous Programming Model或(APM)。 Async-await使用基于Task Based Asynchronous Pattern的更新的异步编程方式。

您可以阅读有关asynchronus编程模式here的更多信息。

有一种方法可以通过TaskFactory.FromAsync方法将支持APM的较旧API转换为较新的API,以将您的方法转换为async方法。

我认为你需要这样的东西:

myRequestState.response = 
       await TaskFactory.FromAsync(
                         request.BeginGetResponse, 
                         request.EndGetResponse,
                         Tuple.Create(myRequestState, actorID));

答案 1 :(得分:0)

如果您使用的是.NET 4.5或更高版本,则HttpWebRequest对象上使用的首选方法是GetResponseAsync(),请参阅here

所以你的网络电话看起来像这样:

//note the use of the async/await
public async Task<Bitmap> DownloadBitmap(/* whatever your params were */)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    //add the rest of the data/params or anything else you need then...
    // await the asynchronous request.

    HttpWebResponse result = (HttpWebResponse)(await request.GetResponseAsync());

    Stream responseStream = myWebResponse.GetResponseStream();
    return new Bitmap(responseStream);
}

并将其称为:

var bitmaps = new List<Bitmap>();

foreach (myObject obj in myObjectList)
{
    bitmaps.Add(await DownloadBitmap(obj.profilePath, obj.id));
}

如果你不想等待,你可以这样做(或者对于.NET 4.0 +):

//note the use of the the slightly older ContinueWith
public Task<Bitmap> DownloadBitmap(/* whatever your params were */)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    //add the rest of the data/params or anything else you need then...
    // await the asynchronous request.

    return request.GetResponseAsync().ContinueWith((antecedent) => {
        HttpWebResponse myWebResponse = (HttpWebResponse)antencedent.Result;
        Stream responseStream = myWebResponse.GetResponseStream();
        return new Bitmap(responseStream);
    });
}

并将其称为:

var tasks = new List<Task>();

foreach (myObject obj in myObjectList)
{
    tasks.Add(DownloadBitmap(obj.profilePath, obj.id));
}

await tasks.WhenAll();

但这不是你要问的问题,你问的是一个异步与否的方法,使用IAsyncCallbacksIAsyncResults的APM(异步编程模型)是有问题的,因为它需要您可以手动配置等待和状态等source

TPL(任务并行库)引入了TAP模式(基于任务的异步模式),通过使用Lambda表达式和{{1}包装等待和状态管理来清理并处理大量繁重工作。对象(我的第二个例子)。

通过允许您直接Task await对象,可以在.NET 4.5中更进一步。而不是需要调用Task(这可能很麻烦,因为Task.Result对象中发生的异常包含在Task对象中,您需要解包/展平以查看实际发生的情况)。

通过在TAP中使用async / await,您再次利用语言中内置的功能自动等待任务,并返回结果,或者在异常的情况下,它会自动解包异常并抛出异常你喜欢普通的同步方法。(除此之外还有更多内容,但就顶级解释而言,就是这么简单 - 有关详细信息,请参阅here。)

最重要的是,它会使您的方法更加混乱,并且可以让您更清楚地了解方法中的内容 - 并使其更易于维护。

最后,实际回答问题 - 是的,使用的行为 AggregateException 异步,但是,它不是一个可以等待的调用,因为它使用回调,而不是将对象返回到等待。

所以,虽然你没有在你的问题中包含它,但我假设你有BeginGetResponse的方法签名,如此:DownloadBitmap但是,你不是在等待那个方法中的任何东西,因为APM模式没有直接暴露句柄等待。

如果你想等待任务(这些天是.NET的首选方法),请考虑使用不同的异步模式。

来自MSDN:

  

异步编程模型(APM)模式(也称为IAsyncResult模式),其中异步操作需要Begin和End方法(例如,BeginWrite和EndWrite用于异步写入操作)。这种模式不再推荐用于新开发。有关更多信息,请参阅异步编程模型(APM)。

     

基于事件的异步模式(EAP),它需要具有Async后缀的方法,并且还需要一个或多个事件,事件处理程序委托类型和EventArg派生类型。 EAP是在.NET Framework 2.0中引入的。它不再被推荐用于新开发。有关更多信息,请参阅基于事件的异步模式(EAP)。

     

基于任务的异步模式(TAP),它使用单个方法来表示异步操作的启动和完成。 TAP是在.NET Framework 4中引入的,是.NET Framework中异步编程的推荐方法。 C#中的async和await关键字以及Visual Basic语言中的Async和Await运算符为TAP添加了语言支持。有关更多信息,请参阅基于任务的异步模式(TAP)。

答案 2 :(得分:0)

  

我知道我在这个函数中没有使用await但是原因   我认为没有必要在任何地方因为BeginGetResponse   已异步?

你是对的,你不需要使用async关键字,因为你正在遵循一个同步的不同的模式。 BeginXXXEndXXX的人称为APM (Asynchronous Programming Model)async-await与更新的模式(TAP (Task-based Asynchronous Pattern))齐头并进。

如果您决定使用TAP,可以更轻松地实现您的目标。

在这种情况下根本不需要任何同步原语。使用HttpClient中存在的现有TAP apis代替:

public async Task<Bitmap> DownloadBitmapAsync(string uri)
{
    var httpClient = new HttpClient();
    var response = await httpClient.GetAsync(uri);
    using (var responseStream = await response.Content.ReadAsStreamAsync())
    {
        return new Bitmap(responseStream);
    }
}

然后同时执行它们:

var bitmapDownloadTasks = myObjectList.Select(obj =>
                                              DownloadBitmapAsync(obj.profilePath));
await Task.WhenAll(bitmapDownloadTasks);

注意我删除了其中一个参数,因为我看不到downloadBitmap方法的方法签名。

这样,您就可以利用现有的TAP apis而不是使用APM模式的包装器自己创建它们。这可以为您节省几行代码,并且可以减少代码的冗长程度。