在“IsAlive”属性为false后,Join拒绝确认子线程已终止。 C#

时间:2011-06-21 14:36:43

标签: c# multithreading join

Nutshell:我从我的表单中启动一个线程,然后一段时间后使用它的Join方法。它终止但我的应用程序停留在Join上,并拒绝承认它已完成加入。什么会导致这种情况发生?我的线程是从我的表单上的一个按钮启动的,并尝试从同一表单上的第二个按钮加入。

更多信息: 我有一个使用线程来完成通信和数字运算的应用程序。假设主窗体是父线程,第一个子节点是Child1。启动时,Child1与外部设备建立了一些通信,并启动了自己的2个子线程(Child2和Child3)来处理传入的数据。

当用户决定应用程序要停止处理传入数据时,我需要Child1终止(因此,如果需要,可以在恢复之前更改com设置)。我设置了一个停止事件,Child1退出了它的执行循环,它做的第一件事就是通知Child2和Child3不再需要它们(通过另一个停止事件),Child2中的Join方法等待Child2和Child3。这很好用。

不起作用的是,在设置了促使Child1退出运行循环并终止的停止事件后,表单也在Child1上使用了Join方法,但是,这个Join无限期地等待。

单步执行:当我单步执行我的应用程序时,我注意到在使用Join之前,IsAlive属性为true。在我点击Child1.Join()后,我再也无法从线程中获取任何信息,因为它位于“JoinWaitSleep”中。但是,如果我运行while循环导致表单线程在Child1.IsAlive为true时休眠,那么这很好。我的第二个按钮是否是某个无法将Child1连接到它的线程的一部分?


public void Run()
{   //Known as Child1
    //code to setup coms is here

    //Launch Builder threads
    InsertBackgroundMonitor("Launching Collector Threads");
    RunBuilders = true;

    //Known as Child2 and Child3
    BuildThread1 = new Thread(new ThreadStart(Cam1Builder));
    BuildThread2 = new Thread(new ThreadStart(Cam2Builder));
    BuildThread1.Start();
    BuildThread2.Start();

    while (!StopEventHandle.WaitOne(0, true))
    {
    //// Code that waits for coms and tosses data into lists
    }

    RunBuilders = false;

    //Wait for threads to terminate
    BuildThread1.Join();
    BuildThread2.Join();
}

private void RunButton_Click(object sender, System.EventArgs e)
{
    //Button for running the control thread
    ControlThread = new Thread(new ThreadStart(Run));
    ControlThread.Start();
}

private void StopButton_Click(object sender, System.EventArgs e)
{
    if (btnStop.Enabled)
    {   //Button for stopping the control thread
        StopEventHandle.Set();

        if (ControlThread != null)
        {
            while (ControlThread.IsAlive)
            {
                Thread.Sleep(100);
            }
            //somehow Join did not work
            //ControlThread.Join();
        }

        //Update buttons
        btnStart.Enabled = true;
        btnStop.Enabled = false;               
    }
}

3 个答案:

答案 0 :(得分:2)

如果没有更多代码,很难说,但是您已经启动了一些资源,但是您没有正确关闭。

Join()等待线程完全关闭,并在返回之前释放所有资源。如果线程有任何BackgroundWorker任务,或者它有任何你没有显示仍在运行的备用任务,它将不会返回。

由于BuildThread1和BuildThread2都正确返回,并且加入了,您可以确定它不是其中之一,也不是他们正在做的任何事情。查看其余代码。它做了什么?

编辑: 这很好用:

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }


    Thread ControlThread;
    Thread BuildThread1;
    Thread BuildThread2;
    volatile bool RunBuilders = true;
    volatile bool RunControl = true;

    private void button1_Click(object sender, EventArgs e)
    {
        ControlThread = new Thread(new ThreadStart(Run));
        ControlThread.Start();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        RunControl = false;

        if (ControlThread != null)
        {
            while (ControlThread.IsAlive)
            {
                Thread.Sleep(100);
            }
            //somehow Join did not work
            ControlThread.Join();
        }
    }

    public void Run()
    {   //Known as Child1
        //code to setup coms is here

        //Launch Builder threads
        RunBuilders = true;

        //Known as Child2 and Child3
        BuildThread1 = new Thread(new ThreadStart(Cam1Builder));
        BuildThread2 = new Thread(new ThreadStart(Cam1Builder));
        BuildThread1.Start();
        BuildThread2.Start();

        while (RunControl)
        {
        //// Code that waits for coms and tosses data into lists
        }

        RunBuilders = false;

        //Wait for threads to terminate
        BuildThread1.Join();
        BuildThread2.Join();
    }


    public void Cam1Builder()
    {
        while ( RunBuilders )
        {

        }
    }
}
}

答案 1 :(得分:1)

我只能猜测导致这种情况的可能问题,因为我没有看到Cam1BuilderCam2Builder方法的代码。 1 立即跳转的可能性对我说:

  • 调用Join阻止执行调用线程。如果这是从UI线程完成的,那么它会停止消息泵。如果工作者试图将消息发布到UI线程(例如,通过Control.Invoke),则两个线程都将死锁。
  • 如果RunBuilders未标记为volatile或未在lock内访问,那么其他线程所感知的值无法在任何给定时间点进行预测。

通常,不建议从UI线程调用Join。 UI线程的特殊之处在于它们运行的​​是一个无限循环,它调度和处理Windows消息。通过调用Application.Run启动此循环,您应该在代码中的某处看到它。如果通过调用Join或其他阻塞方法来阻止UI线程,则它无法处理排队的消息。您可能正在使用Control.Invoke来编组来自其中一个工作线程的UI线程上的委托的执行。如果是这种情况,那么两个线程都会死锁。 Join阻止等待工作线程的UI线程。 Control.Invoke阻止等待UI线程的工作线程。

用作线程之间通信机制的变量应该是线程安全的。在您的情况下,您使用RunBuilders作为在工作线程上发出操作信号的机制。问题是,由于RunBuilders既未标记为volatile,也未从lock块中访问,因此其值无法可靠地传输到工作线程。您可能在一个线程中将其值设置为true,但另一个线程可能会无限期地继续读取false


1 如果您发布更多代码,我可能会提供更好的见解。具体来说,我希望看到Cam1BuilderCam2Builder正在做什么的大致轮廓,特别是它正在阅读的部分RunBuilders

答案 2 :(得分:1)

您是否尝试将RunBuilders更改为volatile?我个人不知道,因为我以这种方式使用bool交叉线程,但如果你要去它应该是volatile,所以所有线程都看到更新而不是由JIT“缓存”的陈旧值。

除此之外,还需要看看其他两个线程在做什么。