【问题标题】:Avoid UI blocking due to lock in WPF避免由于 WPF 中的锁定而导致 UI 阻塞
【发布时间】:2017-02-07 09:45:26
【问题描述】:

我有以下问题。我的软件可以访问通过 USB 连接的硬件。

在 UI 中有一些按钮(例如搜索设备等)

您可以启动一个任务,该任务将锁定资源(USB 设备)并在后台线程上执行耗时的操作。

问题是: 如果在 GUI 中按下需要此资源的按钮,则整个 UI 将被阻塞,直到耗时的后台线程完成(由于锁定)。

我有一些想法如何解决这个问题,但似乎都有很多缺点。

到目前为止我的想法:

  • 使用 Monitor.TryEnter 检查资源是否可以访问。 ##缺点是这需要我更改所有ICommand实现(添加if语句)

  • 创建另一个 ICommand 实现,它有一个锁作为附加参数,并且只有在这个锁空闲时才执行该方法(我希望将它作为预定义的 CanExecute 语句)。 ## 问题是,我不确定 CanExecute 的更改是否会被正确填充(我可以创建一个后台任务来定期检查资源是否可用,但我认为这可能会导致赛车状况......)

我正在寻找如何解决这个问题的想法,如果这不是一个“建设性问题”,请随时标记它。

问候。

【问题讨论】:

  • 在 USB 和 UI 之间再添加一层?就像,在您的视图模型中,您不是直接访问 USB,而是添加一个属性,该属性将在任务完成时在回调中更新。
  • 只需使用简单的布尔标志而不是锁定按钮点击处理程序
  • 为什么你的 UI 上有lock
  • 整个 gui 将被阻止,而不是锁定...更改它
  • ICommand 有一个 CanExecute 属性和一个 CanExecuteChanged 方法。开始操作时,将 CanExecute 设置为 false 并触发事件。操作完成后,将其设置为 true 并触发事件。

标签: c# wpf multithreading mvvm locking


【解决方案1】:

在您的Execute() 中,您可以使用Interlocked.CompareExchange() 在生成您的工人之前设置一个忙碌标志。

稍后在您的CanExecute() 中简单地执行Interlocked.Read() 来测试“忙碌”。

当您的工作人员完成后,只需执行另一个 Interlocked.Exchange() 即可清除您的忙碌标志。

Interlocked 类是线程安全的;相当快;并且上述模式不会阻塞您的 UI。

【讨论】:

    【解决方案2】:

    您可以在关键部分周围使用异步锁。 .NET Framework 提供了具有异步 WaitAsync 方法的 SemaphoreSlim 类:

    System.Threading.SemaphoreSlim _lock = new System.Threading.SemaphoreSlim(1, 1);
    
    //Asynchronously wait to enter the semaphore... 
    await _lock.WaitAsync();
    try
    {
        //do your thing in the critical section
    }
    finally
    {
        _lock.Release();
    }
    

    还有一个 AsyncLock 类型,它是 Stephen Cleary 的 AsyncEx 库中可用的 lock 关键字的异步几乎等效版本,您可以使用:https://github.com/StephenCleary/AsyncEx/wiki/AsyncLock

    使用异步锁,您可以等待锁被释放而不会阻塞 UI 线程。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-14
      • 1970-01-01
      • 2016-01-31
      • 1970-01-01
      • 2013-04-30
      • 1970-01-01
      • 2014-12-29
      • 1970-01-01
      相关资源
      最近更新 更多