互斥锁阻止UI线程

时间:2015-02-02 14:27:36

标签: c# wpf block mutex ui-thread

我的应用程序中有后台线程,它使用这样的互斥:

  void bgWorker_DoWork(object sender, DoWorkEventArgs e)
    {
      MyMutex.MuImageLock.WaitOne();                
      foreach (var file in ImageFiles)
      {
          SyncFileToLocalImage(file.FileNameNoPath);
      }
       MyMutex.MuImageLock.ReleaseMutex();          
    }

同步需要大约2-3分钟。现在我的应用程序中还有一个后台线程,它确实做了同样的事情。

我的问题是,在我开始第二个帖子之前,我想检查一下互斥是否可用。如果它不可用意味着另一个线程正在运行,那么我不需要启动这个新线程。所以要检查我是否使用下面的代码。

但是使用下面的代码,WaitOne方法会阻止我的UI线程,直到前一个线程不释放Mutex。

if (MyMutex.MuImageLock.WaitOne())
{
   getPRImages();
   MyMutex.MuImageLock.ReleaseMutex();
}

我不想阻止我当前的UI线程我只想看到互斥锁是否可用。我该怎么做?

1 个答案:

答案 0 :(得分:1)

在您的代码示例中,除了后台线程本身之外,您似乎没有使用BackgroundWorker的任何功能。所以正确的方法是简单地保留一个标志并使用Task.Run()来启动后台进程:

private bool _isRunning;

private async void Button_Click(object sender, RoutedEventArgs e)
{
    if (_isRunning)
    {
        return;
    }

    _isRunning = true;
    await Task.Run(() => SyncAllFiles());
    _isRunning = false;

    // Populate ListBox here
}

private void SyncAllFiles()
{
    foreach (var file in ImageFiles)
    {
        SyncFileToLocalImage(file.FileNameNoPath);
    }
}

注意:在上面,我假设有一些UI按钮导致同步开始。不幸的是,您的问题没有包含足够完整的代码示例来理解上下文。据推测,如果不是按钮,您可以根据您的具体情况调整上述内容。

即使您正在报告进度并且恰好将其从问题的代码示例中删除,您仍然可以执行上述操作。也可以使用Progress<T>类,将其实例传递给SyncAllFiles()方法,并调用IProgress<T>.Report()方法实际报告进度。这将与BackgroundWorker.ProgressChanged事件的工作方式相同,因为将在UI线程上调用事件处理程序(只要在UI线程上创建Progress<T>对象)。

请注意,甚至比上面更好的方法是简单地禁用允许用户再次启动操作的按钮或其他UI元素,仅在完成后重新启用它。


最后:我认为没有一种实用的方法可以使用Mutex类(您的帖子中没有具体说明,但我假设您使用的是System.Threading.Mutex)。根据互斥体来协调操作和UI线程似乎只是要求难以解决的竞争条件。例如。如果连续按钮上有两个点击事件,在后台线程可以开始并获取互斥锁之前处理第二个事件,该怎么办?这只是它可能出错的许多可能方式之一。

上述提议的解决方案,通过完全在一个线程中管理状态,将确保直接解决所有竞争条件。