2016-03-28 67 views
0

我是C#中的新手,並且我將開發一個使用第三方網絡庫發送請求的小程序。如何隱藏異步調用在C#中同步調用工作

假設存儲在隊列qTasks中的一些請求(只是簡單的字符串),它將按照提交的順序逐個處理這些請求,隊列可以在執行過程中更新,並且應該在任何時候停止有錯誤返回。

我可以只用一個for循環來逐個調用數組中的發送請求命令,但不幸的是sendrequest命令是一個回調OnStageChanged的異步方法,我需要在發送下一個請求之前檢查結果,當狀態是「完成」。

現在,我使用下面的方法來處理它:

在主UI線程,

// Put those request text in a queue names qTasks, then call a goNextTask() to process the request one by one. 
// The queue can be updated by the UI thread at anytime, goNextTask will be called periodically to handle those pending request in the queue. 

private void goNextTask(bool lastSuccess = true) 
{ 
    if (lastSuccess) 
    { 
     if (qTasks.Count > 0) 
     { 
      // continue to next request 
      string requestText = qTasks.Dequeue(); 
      SendRequest(requestText, OnStageChangeHandler); 
     } else { 
      // Report for all request sent successfully 
     } 
    } else { 
     // stop and show error 
    } 
} 

回調方法OnStageChangeHandler將由庫被稱爲每當階段的變化,它完成後將有狀態「完成」。

private void OnStageChangeHandler(object sender, StageChangeEventArgs e) 
{ 
    if (e.newState == SessionStates.Done) 
    { 
     // check result here 
     bool success = <...> 

     // then call the goNextTask in UI thread with the result of current request. 
     Application.Current.Dispatcher.BeginInvoke(
      System.Windows.Threading.DispatcherPriority.Normal, 
      (Action)(() => goNextTask(success))); 
    } 
} 

雖然現在工作得很好,我認爲這是一個有點傻,因爲它有一個有點遞歸流(A - >乙 - > - >乙 - > ....)。

我瞭解到MS已經改進了Web請求處理,以便它可以在同步模式下工作。

我想知道如果我能有一個包裝上作上述異步調用的工作作爲一個同步調用,因此它可以在一個簡單的流程來完成的這樣一個循環:

while (qTaks.Count > 0) 
{ 
    if (!sendAndWaitReturn(qTasks.Dequeue())) { 
     // Report error and quit 
    } 
} 
// all tasks completed 

sendAndWaitReturn方法將發送請求,然後等待狀態「完成」,然後返回結果。

我發現可以使用控制標誌指示當前請求的狀態的一些例子,回調函數會更新這個控制標誌,而使用while循環對這個標誌的UI線程循環:

while (!requestDone); 

因此它不會繼續到nextRequest直到requestDone。但在這種情況下,UI將被阻止。

有沒有更好的方法來轉換異步調用作爲同步調用工作,而不會阻塞UI線程?

+0

你可以舉一個例子說明SendRequest方法正在處理什麼?遞歸是做到這一點的最佳方式。但是,根據邏輯我們可以改變其書面的方式。 – Thanigainathan

+0

該庫提供SendRequest方法。實際上,它是FiddlerCore的SendRequest方法,所需的頭文件和requestBodyBytes是從隊列中的數據生成的。 –

+0

什麼,SendRequest返回?它會返回一個「任務」嗎?但即使你確實做到了同步,你也明白在UI線程上這樣做會凍結UI? –

回答

0

你將遇到的困難是你有矛盾的慾望。一方面,你想避免阻塞UI線程。另一方面,你不想異步運行事情,所以你會阻止UI線程。

你將不得不選擇一個,並且絕對沒有理由繼續同步做事情(特別是在阻止UI線程的情況下)。如果這樣做會傷害,不這樣做。

你還沒有指定,但我猜你是從一個按鈕點擊事件開始這個處理。使該方法通過該異步事件調用該單擊事件。例如:

private async void StartProcessing_Click(object sender, EventArgs e) 
{ 
    await Task.Run(() => StartProcessing()); 
} 

在那裏,您已經開始處理並且UI線程沒有被捆綁。

接下來的事情是,你說得對,讓事件表現得周而復始是愚蠢的。事件是通知某人狀態已經改變,其目標不是管理隊列策略。 隊列應該管理隊列策略(或者如果您不想將其抽象出來,則可以使用方法來處理請求)。

那麼你會怎麼做呢?那麼,你已經說過,SendRequest將會話對象交給調用者。調用者大概是編排隊列策略並確定是否再次調用SendRequest的人。

讓調用者檢查會話對象的有效性並決定是否繼續基於此。

此外,我不熟悉那個特定的庫,但簡要地看了看它的文檔,它也有一個具有相同簽名的SendRequestAndWait()方法,聽起來像它可能更好地滿足您的需求。

+0

感謝克里斯的建議。 實際上,發送請求過程可以由UI按鈕或定時器事件觸發。在應用程序中還有其他進程一起運行,並且它們將在某個特定事件(例如,完成計算過程)後將請求提交給請求隊列。此外,用戶還可以直接在UI中添加請求,這就是爲什麼我不想在發送請求時阻止UI。然後,發送請求過程將在預定義的時間段內在隊列中提交這些請求,或者通過用戶界面中的按鈕點擊來觸發。 –

+0

SendRequestAndWait方法聽起來像我正在尋找的。但我不確定SendRequestAndWait方法是否真的要等到「完成」,因爲它也有OnStageChange的回調,它似乎仍然使用回調方法來通知調用者,但幫助文件沒有詳細信息。也許我會在稍後嘗試。非常感謝。 –

+0

經過測試,SendRequestAndWait真的有效。雖然它仍然使用回調來通知呼叫者,但它會等到狀態「完成」。我使用調試器進行了檢查,在執行到下一個語句之前,它將完成狀態爲「完成」的OnStageChangeHandler。謝謝。 –