线程在加入后不会被唤醒

本文关键字:唤醒 线程 | 更新日期: 2023-09-27 18:15:08

我有一个GUI界面,它有一个开始和一个取消按钮。启动后,主线程(GUI线程)将创建第二个线程,该线程将执行实际工作。当按下取消按钮时,它所做的就是设置一个布尔值,告诉工作线程停止工作并结束。问题是主GUI线程仍然卡住,即使我确信工作线程已经完成了它正在做的事情。为什么呢?

下面是一些代码:

    private Thread workerThread;
    private SomeClass fs;
    private void buttonSearch_Click(object sender, EventArgs e)
    {
      //do some initializations
      fs = new SomeClass();
      workerThread = new Thread(fs.WorkMethod);
      workerThread.Start();
    }
    private void buttonCancel_Click(object sender, EventArgs e)
    {
         fs.StopWork();
         workerThread.Join();
    }

    inside SomeClass:
    private bool keepWorking;
    public void StopWork()
    {
        keepWorking= false;
    }
    public void WorkMethod()
    {
       if (keepWorking)
        {
            //do some stuff with recursion
         }
    }

有人知道为什么主线程调用join后不会醒来吗?我也试过调试,看看会发生什么,如果我手动更改keepWorking变量为false,该方法确实达到了它的"结束"。

线程在加入后不会被唤醒

你的WorkMethod有一个对Invoke的调用,在那里调用一个委托在UI线程上运行,然后阻塞直到它完成。因为你的UI线程目前正在阻塞对Join的调用,等待后台线程,UI线程无法调用该委托。

现在两个线程都在等待另一个线程,并且没有任何进展。这被称为"死锁"。

另外,keepWorking应该被标记为volatile,因为它是从多个线程访问的;因为它的立场,后台线程可以访问该变量的过期/缓存值相当长的一段时间后,主线程改变它。将其标记为volatile可以防止运行时进行此类优化。

这里的解决方案是不要用调用Join来阻塞UI线程。如果你需要在后台线程结束时执行一些代码,那么你需要在线程结束时异步触发这些代码,而不是同步阻塞。