【问题标题】:Avoid execution overlap避免执行重叠
【发布时间】:2016-04-12 16:12:00
【问题描述】:

我有这个代码:

Private Sub NewRecallLabel_TextChanged(sender As Object, e As EventArgs)
    Dim myrecalllabel As Label = TryCast(sender, Label)

    Dim SpeechSynthesizer As New SpeechSynthesizer

    SpeechSynthesizer.Speak("Ticket number " & TTSTicket & ", please proceed to counter " & TTSCounter)

End Sub

发生的情况是,当 TextChanged 事件触发时,UI 会在执行说话代码时冻结。 我知道使用 SpeakAsync 可以解决问题,但是当触发多个 TextChanged 事件时,音频会重叠 - 我不希望这种情况发生。

谁能告诉我如何避免这种情况?

【问题讨论】:

  • 可能和this一样
  • 您的目标是哪个 .NET 版本?
  • 我的目标是 .NET v4 先生。
  • 啊,好的。我已经删除了我的答案,因为它适用于 4.5 及更高版本。虽然 4.0 完全可以实现完全异步版本,但它要麻烦得多。请暂时使用@Gubr 的拦截解决方案。
  • 我尝试了@Gubr 的解决方案,但它冻结了 UI。

标签: vb.net


【解决方案1】:

我们已经确定您使用的是 .NET 4.0,因此 SemaphoreSlim 将退出窗口。

首先,我会尝试通过共享SpeechSynthesizer 的实例并在收到新请求时使用SpeakAsyncCancelAll 来解决此问题:

Private SpeechSynthesizer As New SpeechSynthesizer

Private Sub NewRecallLabel_TextChanged(sender As Object, e As EventArgs)
    Dim myrecalllabel As Label = TryCast(sender, Label)

    SpeechSynthesizer.SpeakAsyncCancelAll()
    SpeechSynthesizer.SpeakAsync("Ticket number " & TTSTicket & ", please proceed to counter " & TTSCounter)
End Sub

原始答案

由于您不能在异步上下文中使用 SyncLock 并且您希望非阻塞执行,因此最好的办法是使用 SemaphoreSlim(1, 1) 来获得异步互斥体语义(类似于 @Gubr 建议的,除了异步) :

Private Semaphore As New SemaphoreSlim(1, 1)

Private Sub NewRecallLabel_TextChanged(sender As Object, e As EventArgs)
    Dim myrecalllabel As Label = TryCast(sender, Label)
    Dim SpeechSynthesizer As New SpeechSynthesizer

    Await Semaphore.WaitAsync()

    Try
      ' We're inside the protected region now.
      Await SpeechSynthesizer.SpeakAsync("Ticket number " & TTSTicket & ", please proceed to counter " & TTSCounter)
    Finally
      Semaphore.Release()
    End Try

End Sub

我还会考虑以某种形式的限制和/或自动取消进行连接,因为如果标签文本快速变化(即每个后续讲话),目前的代码不会很好地工作请求必须等待之前的请求完成)。

【讨论】:

  • 我认为它会起作用,因为 lock 语句被包装在一个被异步调用的普通同步方法中。但也许我错了,因为我说我还没有真正尝试过。你的解决方案看起来很整洁
  • 我收到一个错误:“WaitAsync”不是“System.Threading.SemaphoreSlim”的成员
  • 这太深了!对不起,我是新手。
  • @Gubr, SyncLock 可以在任何同步代码区域中正常工作,但您不能在其中包含 Await 语句。由于最终目标(根据问题)是非阻塞执行,我选择推荐一个同步原语,它支持开箱即用的异步场景。
  • @carlot0820,听起来您正在运行 .NET 4.0 或更低版本。我还刚刚注意到SpeakAsync 也是void(我错误地认为它返回了Task),这使我的上述答案无效。有时间我会重做;暂时请采纳 Gubr 的建议。
【解决方案2】:

也许在异步调用中使用锁将有助于它看起来像这样:

Private Sub NewRecallLabel_TextChanged(sender As Object, e As EventArgs)
    Dim myrecalllabel As Label = TryCast(sender, Label)
    Dim SpeechSynthesizer As New SpeechSynthesizer
        SpeechSynthesizer.SpeakAsync("Ticket number " & TTSTicket & ", please proceed to counter " & TTSCounter)
End Sub

在你的异步调用中使用lock:

Private messagesLock As New Object
SyncLock messagesLock
    SpeechSynthesizer.Speak("the sync one")
End SyncLock

我没有尝试过,但你明白了。

【讨论】:

    【解决方案3】:

    我能够使用BackgroudWorker 获得我想要的东西。我将SpeakAsync 代码放到BackgroudWorker 并替换了

    SpeechSynthesizer.SpeakAsync("Ticket number " & TTSTicket & ", please proceed to counter " & TTSCounter)

          If SpeechBackgroundWorker.IsBusy = False Then
    
             TTSCounter = CRichTextBox.Text
             TTSTicket = TRichTextBox.Text
    
             SpeechBackgroundWorker.RunWorkerAsync()
    
             Exit Sub
    
          End If
    

    它成功地将演讲排队并且不影响 UI 线程。希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2018-02-12
      • 2015-02-08
      • 2017-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-05
      • 2020-10-11
      • 2023-04-06
      相关资源
      最近更新 更多