簡體   English   中英

這是異步運行嗎?

[英]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中使用了一種較舊的異步編程范例,稱為異步編程模型或(APM)。 Async-await使用一種新的方式進行異步編程,該方法基於基於任務的異步模式

您可以在此處閱讀有關異步編程模式的更多信息。

有一種方法可以通過TaskFactory.FromAsync方法將支持APM的較舊API轉換為較新的API,以將您的方法轉換為async方法。

我認為您需要這樣的東西:

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

如果使用的是.NET 4.5或更高版本,則在HttpWebRequest對象上使用的首選方法是GetResponseAsync()請參見此處。

因此,您的網絡通話如下所示:

//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(異步編程模型)是有問題的,因為它需要您手動配置等待和狀態等信息

TPL(任務並行庫)引入了TAP模式(基於任務的異步模式),通過將等待和狀態管理與Lambda表達式和Task對象包裝在一起,為您清理並處理了許多繁重的Task (第二個示例) 。

通過允許您直接await Task對象,可以在.NET 4.5中更進一步。 而不是需要調用Task.Result (這可能很麻煩,因為Task對象中發生的異常都包裝在AggregateException對象中,您必須對其進行展開/展開以查看實際發生了什么)。

通過在TAP中使用async / await,您將再次利用該語言中內置的功能,這些功能會自動等待任務並返回結果,或者在出現異常的情況下,它會自動打開異常包裝並將其拋出給您一個普通的同步方法(它還不止於此,但是就頂級解釋而言,就是這么簡單-有關更多詳細信息,請參見此處 。)

最重要的是,它還可以使您的方法擺脫混亂,並讓您更清楚地了解方法中發生的事情-並使它更易於維護。

最后,要實際回答這個問題 -是的,使用BeginGetResponse的動作異步的,但是,您不能等待該調用,因為它使用了回調,而不是使對象返回等待狀態。

因此,雖然您沒有在問題中包含它,但我假設您具有DownloadBitmap的方法簽名,如下所示: public async Task DownloadBitmap(/* args */)但是,您沒有在該方法中等待任何東西,因為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)。

我知道我沒有在此函數中使用await ,但是我認為在任何地方都不需要它的原因是因為BeginGetResponse已經是異步的?

你說得對,你不需要使用async因為你下面一個不同的模式,這也是異步的關鍵字。 具有BeginXXXEndXXX的那個稱為APM(異步編程模型) async-await與稱為TAP(基於任務的異步模式)的更新模式並駕齊驅。

如果您決定使用TAP,則有一種更輕松的方法來實現您要完成的任務。

在這種情況下,根本不需要任何同步原語。 使用HttpClient中存在的現有TAP HttpClient代替:

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模式的包裝程序自己創建它們。 這樣可以節省幾行代碼,並可以減少代碼的冗長程度。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM