异步方法

时间:2017-01-22 18:45:02

标签: c# asp.net-mvc asynchronous

我正在使用我尝试使用的aync方法遇到一些意外/不需要的行为。异步方法是RecognizeAsync。我无法等待此方法,因为它返回void。发生了什么,是ProcessAudio方法将首先被调用,并且似乎将完成,但网页永远不会返回我的"联系人"查看它应该或错误。方法运行完成后,我的处理程序中的断点开始被击中。如果我让它一直完成,那么在Chrome调试器的网络选项卡中就不会发生重定向," status"将保持标记为待定,并挂在那里。我相信我的问题是由异步性问题引起的,但却无法确定它究竟是什么。

感谢所有帮助。

[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)断点将被触发,但仍然没有发生重定向。我从未被定向到我的联系页面。我就像以前一样无限期地使用原始页面。

2 个答案:

答案 0 :(得分:6)

你太早了。到达return View行时,语音引擎可能还没有开始。

您需要等到语音引擎触发最终事件。最好的方法是将基于事件的异步转换为TAP-based asynchrony

这可以通过使用TaskCompletionSource<T>

来实现

让我们处理(我相信)应该是speechEngine.RecognizeAsync被调用后的最后一个事件,即SpeechRecognized。我假设这是在语音引擎计算最终结果时触发的事件。

所以,首先:

var tcs = new TaskCompletionSource<EventArgs>();

现在可以使用内联lambda样式方法声明在SpeechRecognized触发时将其挂起来完成:

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

(...等等......如果没有识别出语音会发生什么?我们还需要连接SpeechRecognitionRejected事件并为此类事件定义一个自定义的Exception子类...这里我我只是称它为RecognitionFailedException。现在我们正在捕获识别过程的所有可能结果,所以我们希望TaskCompletionSource能在所有结果中完成。)

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

然后

speechEngine.RecognizeAsync(RecognizeMode.Multiple);

现在,我们可以await Task的{​​{1}}属性:

TaskCompletionSource

对EventArgs进行一些处理,这是Task的结果,并将可行的结果返回给客户端。

在执行此操作的过程中,您正在创建需要正确处置的try { var eventArgs = await tcs.Task; } catch(RecognitionFailedException ex) { //this would signal that nothing was recognized } 个实例。

所以:

IDisposable

答案 1 :(得分:0)

如果有人好奇 - 我通过以下方式解决了我的问题:

我改为使用Recognize()而不是RecognizeAsync(..)导致InvalidOperationException,因为异步事件试图在页面生命周期中的无效时间执行#34; 。为了解决这个问题,我将操作包装在一个线程中,并在运行后直接将线程连接回主线程。代码如下:

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

      }
 }