2017-01-22 60 views
1

我正在使用我嘗試使用的aync方法遇到一些意外/不想要的行爲。異步方法是RecognizeAsync。我無法等待此方法,因爲它返回void。發生什麼事情是,ProcessAudio方法將首先被調用,並將看似運行到完成,但網頁永遠不會返回我的「聯繫人」視圖,因爲它應該或錯誤了。該方法運行完成後,我的處理程序中的斷點開始被擊中。如果我讓它播放完成,將不會發生重定向 - 在Chrome調試器的網絡選項卡中,「狀態」將保持標記爲掛起狀態並且只是掛在那裏。我相信我的問題是由異步問題引起的,但一直未能發現它究竟是什麼。異步方法中的意外行爲

所有幫助表示讚賞。

[HttpPost] 
public async Task<ActionResult> ProcessAudio() 
{ 
    SpeechRecognitionEngine speechEngine = new SpeechRecognitionEngine(); 
    speechEngine.SetInputToWaveFile(Server.MapPath("~/Content/AudioAssets/speechSample.wav")); 
    var grammar = new DictationGrammar(); 
    speechEngine.LoadGrammar(grammar); 

    speechEngine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(SpeechRecognizedHandler); 
    speechEngine.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(SpeechHypothesizedHandler); 

    speechEngine.RecognizeAsync(RecognizeMode.Multiple); 

    return View("Contact", vm); //first breakpoint hit occurs on this line 
            //but it doesnt seem to be executed? 
} 

private void SpeechRecognizedHandler(object sender, EventArgs e) 
{ 
    //do some work 
    //3rd breakpoint is hit here 
} 

private void SpeechHypothesizedHandler(object sender, EventArgs e) 
{ 
    //do some different work 
    //2nd breakpoint is hit here 
} 

更新:根據建議,我已經改變了我的代碼(在ProcessAudio):

using (speechEngine) 
{ 
    speechEngine.SetInputToWaveFile(Server.MapPath("~/Content/AudioAssets/speechSample.wav")); 
    var grammar = new DictationGrammar(); 
    speechEngine.LoadGrammar(grammar); 

    speechEngine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(SpeechRecognizedHandler); 
    speechEngine.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(SpeechHypothesizedHandler); 
    var tcsRecognized = new TaskCompletionSource<EventArgs>(); 
    speechEngine.RecognizeCompleted += (sender, eventArgs) => tcsRecognized.SetResult(eventArgs); 

    speechEngine.RecognizeAsync(RecognizeMode.Multiple); 
    try 
    { 
     var eventArgsRecognized = await tcsRecognized.Task; 
    } 
    catch(Exception e) 
    { 
     throw (e); 
    } 
} 

,這是導致一些錯誤的行爲: 的return View("Contact",vm)斷點現在將擊中後的處理程序已完成發射,但仍然沒有發生任何重定向。我從未被定向到我的聯繫頁面。我只是像以前一樣無限期地仔細檢查我的原始頁面。

+0

爲什麼不等'speechEngine.RecognizeAsync(RecognizeMode.Multiple);'? –

+0

嘗試「await」時返回void的異步方法時發生編譯器錯誤 – GregH

+0

@ErikPhilips這是舊式基於事件的asyncrony,它使用與新型樣式基於TAP的異步相交的命名約定。 'WebClient'也分享了這個不幸的命名衝突。 – spender

回答

0

如果有人curious-我解決我的問題,通過執行以下操作:

我改爲使用Recognize()代替RecognizeAsync(..)從而導致InvalidOperationException由於異步事件試圖將在「頁面生命週期中的無效時間」執行。爲了克服這個問題,我將我的操作包裝在一個線程中,並在運行後直接將線程加入主線程。代碼如下:

using (speechEngine) 
     { 
     var t = new Thread(() => 
     { 
      speechEngine.SetInputToWaveFile(@"C:\AudioAssets\speechSample.wav"); 
      speechEngine.LoadGrammar(dictationGrammar); 

      speechEngine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(SpeechRecognizedHandler); 
      speechEngine.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(SpeechHypothesizedHandler); 
      speechEngine.Recognize(); 
     }); 
     t.Start(); 
     t.Join(); 

     } 
} 
5

你太早了。語音引擎可能還沒有開始,當你點擊return View線。

您需要等到終止事件從語音引擎中被觸發。最好的辦法是將基於異步的事件轉換爲TAP-based asynchrony

這可以通過使用TaskCompletionSource<T>

讓我們處理(我認爲)應該speechEngine.RecognizeAsync火後的最後一個事件來實現調用,即SpeechRecognized。我假設這是當語音引擎計算出最終結果時觸發的事件。

所以,首先:

var tcs = new TaskCompletionSource<EventArgs>(); 

現在讓我們把它掛到完成時SpeechRecognized被觸發,使用內聯拉姆達式方法聲明:

speechEngine.SpeechRecognized += (sender, eventArgs) => tcs.SetResult(eventArgs); 

(...等待...如果沒有語音被識別,會發生什麼?我們還需要連接SpeechRecognitionRejected事件併爲這種類型的事件定義一個自定義的Exception子類...在這裏,我將其稱爲RecognitionFailedException現在我們正在捕獲所有可能的結果的認可過程,s ·我們希望這對TaskCompletionSource將在所有結果完成)

speechEngine.SpeechRecognitionRejected += (sender, eventArgs) => 
          tcs.SetException(new RecognitionFailedException()); 

然後

speechEngine.RecognizeAsync(RecognizeMode.Multiple); 
現在

,我們可以await我們TaskCompletionSourceTask性質:

try 
{ 
    var eventArgs = await tcs.Task; 
} 
catch(RecognitionFailedException ex) 
{ 
    //this would signal that nothing was recognized 
} 

做一些處理作爲任務結果的EventArgs,並將可行結果返回給客戶端。

在這樣做的過程中,您正在創建IDisposable實例,需要妥善處置。

所以:

using(SpeechRecognitionEngine speechEngine = new SpeechRecognitionEngine()) 
{ 
    //use the speechEngine with TaskCompletionSource 
    //wait until it's finished 
    try 
    { 
     var eventArgs = await tcs.Task; 
    } 
    catch(RecognitionFailedException ex) 
    { 
     //this would signal that nothing was recognized 
    } 

} //dispose 
+0

我已更新我的回答以反映您的建議更改。請參閱我遇到的問題。這是你的想法嗎? – GregH

+0

@peggy我假設這是因爲有不止一個'SpeechHypothesized'事件。你可以拋棄你對「SpeechHypothesized」事件的處理,只處理我回答中列出的事件(即那些代表**最終**結果的事件,而不是某種中間結果)如果這讓你在某個地方,我們可以弄清楚如何處理SpeechHypothesized事件。 – spender

+0

我已經更新了我目前的嘗試。現在在視圖試圖返回之前讓處理程序啓動(並且我的所有模型和我的虛擬主機看起來都很完美),但由於某種原因,我仍然沒有重定向到聯繫頁面 – GregH