Join拒绝承认子线程在;IsAlive“;属性是假的。C#
简而言之:我从我的表单中启动一个线程,然后稍后在它上使用Join方法。它终止了,但我的应用程序在加入时被卡住了,并且拒绝承认它已经完成了加入。什么会导致这种情况发生?我的线程从窗体上的按钮启动,并尝试从同一窗体上的第二个按钮加入 更多信息: 我有一个应用程序,它使用线程来完成通信和数字运算。假设主窗体是父线程,第一个子线程是Child1。启动Child1后,会与外部设备建立一些通信,并启动自己的两个子线程(Child2和Child3)来处理传入数据 当用户决定应用程序停止处理传入数据时,我需要Child1终止(因此,如果需要,可以在恢复之前更改com设置)。我设置了一个停止事件,Child1退出它的执行循环,它做的第一件事是通知Child2和Child3不再需要它们(通过另一个停止事件),Child2和Child3通过Child1中的Join方法等待。这个很好用 不起作用的是,表单在设置停止事件(该事件提示Child1退出其运行循环并终止)后,还对Child1使用Join方法,但是,此联接将无限期地等待 逐步通过:当我逐步通过我的应用程序时,我注意到在使用Join之前,IsAlive属性为true。在我点击Child1.Join()之后,我无法再从线程中获取任何信息,因为它处于“JoinWaitSleep”状态。但是,如果我运行while循环,使窗体线程在Child1.IsAlive为true时休眠,则效果很好。我的第二个按钮是否是无法将Child1连接到它的线程的一部分Join拒绝承认子线程在;IsAlive“;属性是假的。C#,c#,multithreading,join,C#,Multithreading,Join,简而言之:我从我的表单中启动一个线程,然后稍后在它上使用Join方法。它终止了,但我的应用程序在加入时被卡住了,并且拒绝承认它已经完成了加入。什么会导致这种情况发生?我的线程从窗体上的按钮启动,并尝试从同一窗体上的第二个按钮加入 更多信息: 我有一个应用程序,它使用线程来完成通信和数字运算。假设主窗体是父线程,第一个子线程是Child1。启动Child1后,会与外部设备建立一些通信,并启动自己的两个子线程(Child2和Child3)来处理传入数据 当用户决定应用程序停止处理传入数据时,我需要C
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;
}
}
由于我没有看到
Cam1Builder
和Cam2Builder
方法的代码,因此我只能猜测可能导致这种情况的问题。1立即跳出来的可能性有:
- 调用
会阻止调用线程的执行。如果这是从UI线程完成的,那么它会停止消息泵。如果工作线程试图将消息发布到UI线程(例如通过Join
),则两个线程都将死锁Control.Invoke
- 如果
未标记为RunBuilders
或未在volatile
内访问,则其他线程无法在任何给定时间点预测其值lock
Join
。UI线程的特殊之处在于,它们运行的是一个不确定的循环,即调度和处理windows消息。这个循环是通过调用应用程序启动的。运行你应该在代码中看到的。如果通过调用Join
或其他阻止方法阻止UI线程,则它无法处理排队的消息。您可能正在使用Control.Invoke
从一个工作线程封送UI线程上委托的执行。如果是这种情况,那么两个线程都将死锁<代码>连接
阻止等待工作线程的UI线程<代码>控制。调用
阻止工作线程等待UI线程
用作线程间通信机制的变量应该是线程安全的。在本例中,您使用RunBuilders
作为工作线程上的动作信号机制。问题在于,由于RunBuilders
既没有标记为volatile
,也没有从lock
块中访问,因此其值无法可靠地传输到工作线程。您可以在一个线程中将其值设置为true
,但另一个线程可以无限期地继续读取false
1如果您发布更多代码,我可能能够提供更好的洞察力。具体来说,我想了解一下
Cam1Builder
或Cam2Builder
正在做什么,特别是它正在阅读的部分RunBuilders
,如果没有更多的代码,很难说清楚,但是您已经开始使用一些资源,但是您没有正确关闭它们
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 )
{
}
}
}
}
您是否尝试将RunBuilder更改为volatile?就个人而言,我不知道我是否会以这种方式跨线程使用bool,但如果要使用bool,它应该是可变的,以便所有线程都能看到更新,而不是JIT“缓存”的过时值
除此之外,我们还需要看看其他两个线程在做什么。我们可以看一个简单的示例代码吗?我添加了一些示例代码。我删掉了所有的COM,没有包括Child2和Child3的实际内容。他们都结束得很好,虽然我通过他们的连接没有任何担心。它们的退出是由RunBuilders=false引起的。RunBuilders是否易变?否。应该是吗?RunBuilder仅由Child1更改,并且仅由Child2和Child3读取,但它们的联接方法都正常返回。好的,InsertBackgroundMonitor做什么?我不知道这个函数,谷歌搜索让我觉得它不是任何标准函数。我的Cam1Builder和Cam2Builder线程执行的任务几乎相同,区别在于它们监视两个不同的输入缓冲区。它们对RunBuilder的唯一用途是它们的包含循环,这只是一种声明