2017-01-22 2 views
1

Ich erfahre derzeit unerwartetes/unerwünschtes Verhalten mit einer Synchronisierungsmethode, die ich versuche zu verwenden. Die asynchrone Methode ist RecognizeAsync. Ich kann diese Methode nicht erwarten, da sie ungültig wird. Was passiert, ist, dass ProcessAudio Methode zuerst aufgerufen wird und scheinbar bis zum Ende ausgeführt wird, aber die Webseite nie meine "Kontakt" -Ansicht zurückgibt, wie es sollte oder Fehler aus. Nachdem die Methode vollständig ausgeführt wurde, werden die Haltepunkte in meinen Handlern gestartet. Wenn ich es bis zum Ende durchspielen lasse, wird es niemals eine Umleitung geben - auf der Netzwerk-Registerkarte im Chrome-Debugger bleibt der "Status" als ausstehend markiert und hängt einfach dort. Ich glaube, dass mein Problem durch Probleme mit der Asynchronität verursacht wurde, aber ich konnte nicht herausfinden, was genau das ist.Unerwartetes Verhalten in der asynchronen Methode

Alle Hilfe ist willkommen.

[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 
} 

UPDATE:

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); 
    } 
} 

und dies in einem gewissen falschen Verhalten resultierende: basierend auf Anregungen, ich meinen Code (in ProcessAudio) geändert haben Der return View("Contact",vm) Breakpoint wird nun nach dem Treffer werden Handler sind fertig feuern, aber es gibt immer noch keine Umleitung, die jemals passiert. Ich bin nie auf meine Kontaktseite verwiesen. Ich habe meine Originalseite auf unbestimmte Zeit genau wie vorher.

+0

Warum nicht 'erwarten speechEngine.RecognizeAsync (RecognizeMode.Multiple);'? –

+0

Compiler-Fehler beim Versuch, eine asynchrone Methode abzufragen, die void zurückgibt – GregH

+0

@ErikPhilips Es handelt sich um eine ereignisbasierte asynchrone Methode im alten Stil, die eine Namenskonvention verwendet, die sich mit der neueren TAP-basierten Asynchronität überschneidet. 'WebClient' teilt auch diesen unglücklichen Namenskonflikt. – spender

Antwort

0

wenn jemand curious- ist gelöst i mein Problem, indem Sie die folgenden Aktionen ausführen:

Ich änderte Recognize() statt RecognizeAsync(..) zu verwenden, die aufgrund asynchroner Ereignisse zu InvalidOperationException führen versuchen, zu sein Wird zu einem "ungültigen Zeitpunkt im Seitenlebenszyklus" ausgeführt. Um dies zu umgehen, habe ich meine Operationen in einen Thread eingepackt und den Thread direkt nach dem Ausführen wieder mit dem Hauptthread verbunden. Code unten:

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

Sie gehen zu früh. Die Sprach-Engine hat wahrscheinlich noch nicht einmal angefangen, als Sie die return View Zeile erreichten.

Sie müssen warten, bis das letzte Ereignis von der Sprach-Engine ausgelöst wird. Der beste Ansatz wäre die Konvertierung von der ereignisbasierten Asynchronität in TAP-based asynchrony.

Dies kann mit durch die Verwendung TaskCompletionSource<T>

Lassen Sie uns viel erreicht werden (was ich glaube) sollte das letzte Ereignis sein Feuer nach speechEngine.RecognizeAsync genannt wird, das heißt SpeechRecognized. Ich gehe davon aus, dass dies das Ereignis ist, das ausgelöst wird, wenn das Endergebnis von der Sprach-Engine berechnet wurde.

Also, zuerst:

var tcs = new TaskCompletionSource<EventArgs>(); 

lässt es jetzt anschließen zu vervollständigen, wenn SpeechRecognized abgefeuert wird, Inline-Lambda-Stil Methode Erklärung mit:

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

(... warten ... Was passiert, wenn keine Sprache erkannt wurde? Wir müssen auch das Ereignis SpeechRecognitionRejected verknüpfen und eine benutzerdefinierte Exception-Unterklasse für diesen Ereignistyp definieren ... hier nenne ich es einfach RecognitionFailedException. Jetzt erfassen wir alle möglichen Ergebnisse des Erkennungsprozesses, s o würden wir hoffen, dass die TaskCompletionSource in allen Ergebnissen abschließen würde)

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

dann

speechEngine.RecognizeAsync(RecognizeMode.Multiple); 

jetzt können wir die Task Eigentum unserer TaskCompletionSourceawait.

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

einige tun Verarbeitung der EventArgs, die das Ergebnis der Task ist, und Rückgabe eines realisierbaren Ergebnisses an den Client.

Währenddessen erstellen Sie IDisposable Instanzen, die ordnungsgemäß entsorgt werden müssen. So

:

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

Ich habe meine Antwort aktualisiert, um Ihre vorgeschlagenen Änderungen widerzuspiegeln. Bitte lesen Sie die Probleme, die ich damit habe. Ist das die Implementierung, die Sie im Sinn hatten? – GregH

+0

@peggy Ich gehe davon aus, dass dies daran liegt, dass es mehr als ein "SpeechHypothesized" -Ereignis gibt. Können Sie Ihre Versuche, sich mit dem 'SpeechHypothesized'-Ereignis zu befassen, ablegen und sich einfach mit den in meiner Antwort beschriebenen Ereignissen befassen (dh solche, die ein ** endgültiges ** Ergebnis darstellen, kein Zwischenergebnis) kann herausfinden, wie man mit den "SpeechHypothesized" -Ereignissen umgeht. – spender

+0

Ich habe meinen aktuellen Versuch noch einmal aktualisiert. Jetzt die Handler zu feuern, bevor die Sicht versucht zurückgegeben zu werden (und alle meine Modelle und mein vm scheinen perfekt zu sein), aber ich bekomme immer noch keine Umleitung zur Kontaktseite aus irgendeinem Grund – GregH