【问题标题】:How to stop a play and return right values in TPL?如何在 TPL 中停止播放并返回正确的值?
【发布时间】:2014-12-25 08:17:06
【问题描述】:

在我的 TPL 应用程序中,我想通过 PlayTTS(string text) 方法播放文本到语音。

public static CancellationTokenSource cTokenSource = new CancellationTokenSource();
public static CancellationToken cToken = cTokenSource.Token;

然后在消费者方法中。

async Task Consumer()
    {
        try
        {
            var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = 50,
                CancellationToken = cToken
            };
            var consumerBlock = new ActionBlock<AppointmentReminder>(
            remainder =>
            {
               if (cToken.IsCancellationRequested)
                  return;
                Dictionary<string, string> dict = new OutboundDial(ts).RunScript(remainder, cToken);
                // update UI by the returned dictionary
            },
            executionDataflowBlockOptions);

            m_bufferBlock.LinkTo(
            consumerBlock, new DataflowLinkOptions { PropagateCompletion = true });
            await consumerBlock.Completion;
        }

我有一个按钮事件来取消进程(WPF)。

private void Cancel_Click(object sender, RoutedEventArgs e)
{
    cTokenSource.Cancel();
}

您会看到ActionBlock 中有cToken.IsCancellationRequested,但是尽管我传入了取消令牌,但它并没有帮助在方法OutboundDial(ts).RunScript(remainder, cToken); 中停止进程。

现在我们来看RunScript的方法。

public Dictionary<string, string> RunScript(AppointmentReminder callData, CancellationToken cToken)
    {
        try
        {
             m_ChannelResource = m_TelephonyServer.GetChannel() as SipChannel;
             m_VoiceResource = m_ChannelResource.VoiceResource;
             // Many logging
             // Dial out

            MakeTest(callData, cToken);
        }
        catch
         {throw;}
        finally
        {
            // destroy m_ChannelResource and m_VoiceResource
         }
        return dict;
    }

关键是方法MakeTest,里面有PlayTTS;

public void MakeTest(AppointmentReminder callData, CancellationToken cToken)
{
    try
    {
        if (!cToken.IsCancellationRequested)
        {
            m_VoiceResource.PlayTTS(callData.Text);  
        }
        else
        {
            cToken.ThrowIfCancellationRequested();
            m_VoiceResource.Stop(); // Stops any current activity on m_VoiceResource.
            dict["ConnectedTime"] = " no connection";
            dict["DialingResult"] = " cancellation";
        }

当我单击取消按钮时,我当前的代码没有到达m_VoiceResource.Stop() 部分。 所以我的问题是当cTokenSource.Cancel();,如何让代码运行在:

m_VoiceResource.Stop(); // Stops any current activity on m_VoiceResource.
dict["ConnectedTime"] = " no connection";
dict["DialingResult"] = " cancellation";

编辑:2014 年 10 月 31 日下午 1:10

根据Servy的评论,我使用了cToken.Register(() =&gt; m_VoiceResource.Stop());

我在OneDrive 创建了一个类似的演示。

【问题讨论】:

  • m_VoiceResponse.Stop() 永远不应执行,因为如果请求取消,您将在上一行抛出异常
  • @BobVale,请从 OneDrive 播放我的演示,您会发现问题所在。它只是使用了Console.WriteLine()。(完全没有例外)。
  • cToken.ThrowIfCancellationRequested() 将引发异常
  • @BobVale,我删除了那行但同样的事情。如果你想看看我在onedrive.live.com/… 上的干净源代码,你就会明白问题出在哪里了。

标签: c# task-parallel-library c#-5.0


【解决方案1】:

最后我在 Microsoft MVP 的帮助下找到了解决方案。 Servy 的解决方案很漂亮,但有缺陷。

cToken.Register(() => m_VoiceResource.Stop());

注册应该放在开始播放之前而不是之后。

编辑: 2014 年 11 月 10 日上午 8:09

一个更好的:

 cToken.Register(() => m_VoiceResource.Stop());
 return;

【讨论】:

  • 如果你在开始播放之前注册了回调,那么它可以被调用,在播放器开始之前停止播放器,然后在播放器不再停止之后启动它。
  • @Servy,那我想传两个token,一个是之前的,一个是之后的。我可以吗?
  • 没有理由这样做。如果令牌实际上在您开始之前被取消,那么之前注册的令牌将不会做任何事情。如果取消是在它开始之后发生的,那么它们都会做同样的事情。根本没有理由在启动播放器之前注册回调。
  • @Servy,你说的现象确实存在。但是,如果注册是在播放之后。它不能停止播放,除非把它放在它前面。例如,队列中只有一项。它一直在播放。
  • 如果播放一开始就无法停止,那么注册回调根本没有意义。无论您是在之前还是之后执行此操作,如果播放器一旦启动就无法停止,那么您将永远不会比您在开始播放之前已经拥有的if (!cToken.IsCancellationRequested) 检查更好。
【解决方案2】:

在您检查令牌是否已取消并开始播放后,您将取消令牌。您永远不会返回并再次执行该条件以停止。

你需要做的只是注册一个回调到令牌的取消来停止播放器:

cToken.Register(() => m_VoiceResource.Stop());

在您开始游戏后立即添加该注册。

【讨论】:

  • 我不知道为什么它不起作用。代码确实到达了Stop,但是是在播放整个文本之后。它似乎等待了一点时间。通过单步执行代码,假设队列中有三个项目,我发现 PlayTTS 被执行了 3 次,然后 m_VoiceResource.Stop() 也被执行了 3 次。
  • @Love 那么听起来你的问题出在你使用的任何工具上(我可能会添加一个你没有提供任何信息的工具)。当您致电Stop 时,您需要了解为什么它没有停止工作。
  • 服务。我的意思是单击取消按钮后无法立即执行回调。它应该由 MSDN 跳进去。
  • 我不知道,但也许不是。我可以在 PlayTTS 时使用鼠标播放窗口。(WPF 应用程序)我进入按钮单击事件,它确实到达了那里。回调不在 windows 类中,它在我的自定义类中。(参见我的代码)
  • 伙计们,相信我。我在onedrive.live.com/… 创建了一个演示。文件名为“CancellationTokenIsNotWorking.zip”。项目原来是一个wpf项目,我把项目的属性改成console,这样就可以在屏幕上输出结果了。您需要做的就是单击开始按钮,然后单击取消按钮,但它无法在屏幕上停止。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2013-12-29
  • 2016-06-01
  • 1970-01-01
  • 2011-12-25
  • 1970-01-01
  • 2016-08-17
相关资源
最近更新 更多