2017-01-22 64 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