这是 运行 异步吗?

Is this running Asynchronously?

我目前有:

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?

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

BeginGetResponse 是异步的,但是它使用 .NET 中称为 Asynchronous Programming Model or (APM). Async-await uses a newer way of doing asynchronous programming which is based on the Task Based Asynchronous Pattern 的旧异步编程范例。

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

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

我想你需要这样的东西:

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

如果您使用的是 .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 表达式和 Task对象(我的第二个例子)。

这可以在 .NET 4.5 中更进一步,允许您直接 await Task 对象。而不是需要调用 Task.Result( 这可能会很麻烦,因为 Task 对象中发生的异常被包装在 AggregateException 对象中,您必须 unwrap/flatten 才能看到什么真的发生了)。

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

最重要的是,它还使您的方法变得杂乱无章,让您可以更清楚地看到方法中正在发生的事情 - 并使其更易于维护。

最后,实际回答问题 - 是的,使用 BeginGetResponse 异步的,但是,它不是您可以等待的调用,因为它使用回调,而不是 return 等待对象。

所以虽然你没有在你的问题中包含它,但我假设你有 DownloadBitmap 的方法签名,如下所示:public async Task DownloadBitmap(/* args */) 但是,你没有等待任何东西方法,因为 APM 模式不直接公开要等待的句柄。

如果您想等待任务(目前 .NET 的首选方法),请考虑使用不同的异步模式。

来自 MSDN:

Asynchronous Programming Model (APM) pattern (also called the IAsyncResult pattern), where asynchronous operations require Begin and End methods (for example, BeginWrite and EndWrite for asynchronous write operations). This pattern is no longer recommended for new development. For more information, see Asynchronous Programming Model (APM).

Event-based Asynchronous Pattern (EAP), which requires a method that has the Async suffix, and also requires one or more events, event handler delegate types, and EventArg-derived types. EAP was introduced in the .NET Framework 2.0. It is no longer recommended for new development. For more information, see Event-based Asynchronous Pattern (EAP).

Task-based Asynchronous Pattern (TAP), which uses a single method to represent the initiation and completion of an asynchronous operation. TAP was introduced in the .NET Framework 4 and is the recommended approach to asynchronous programming in the .NET Framework. The async and await keywords in C# and the Async and Await operators in Visual Basic Language add language support for TAP. For more information, see Task-based Asynchronous Pattern (TAP).

I understand that I am not using await in this function but the reason I thought it wasn't necessary anywhere was because BeginGetResponse is already asynchronous?

你说得对,你 不需要 使用 async 关键字,因为你遵循的是 不同的 模式这也是异步的。带有 BeginXXXEndXXX 的称为 APM (Asynchronous Programming Model). async-await goes hand in hand with a newer pattern, called TAP (Task-based Asynchronous Pattern)

如果您决定使用 TAP,有一种更简单的方法可以实现您的目标。

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

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 api,而不是使用 APM 模式的包装器自己创建它们。这可以为您节省几行代码,并可以减少代码的冗长程度。