我面临着C#.NET应用程序中线程之间通信的问题。 希望有人能指导我正确的解决方案。
我在C#.NET中有一个应用程序。它是一个Windows窗体应用程序。 我的应用程序有两个线程 - 一个线程是主线程(UI线程),另一个是子线程。让我们调用子线程“workerThread” 应用程序中只使用了一个表单。让我们将此表单称为“MainForm”
当MainForm加载时启动子线程(使用表单的“Load”事件处理程序启动线程)
在MainForm类中,我有一个名为“stopWork”的变量,它是一个公共布尔变量,它用作一个标志来指示子线程是继续工作还是应该停止
我有另一个类(除了MainForm类),它包含我在子线程中执行的方法。让我们将第二类称为“WorkerClass”。 我将对当前表单(MainForm)的引用传递给“WorkerClass”的构造函数
我在主窗体中有一个“停止”按钮,如果单击它,则将“stopWork”设置为“true”,然后调用“workerThread.Join()”等待子线程完成执行。
在子线程中,方法“doWork”不断检查 for 循环中“parentForm.stopWork”的状态。如果“stopWork”设置为“true”,则循环中断,随后方法结束。
现在,问题是,一旦我点击“停止”按钮,应用程序就会挂起。
我正在粘贴以下代码的部分内容,以便更容易理解:
public partial class MainForm : Form
{
Thread workerThread = null;
ThreadStart workerThreadStart = null;
WorkerClass workerClass = null;
public bool stopWork = true;
/*.......... some code ............*/
private void MainForm_Load(object sender, EventArgs e)
{
workerThreadStart = new ThreadStart(startWork);
workerThread = new Thread(workerThreadStart);
stopWork = false;
workerThread.Start();
}
private void startWork()
{
workerClass = new WorkerClass(this);
}
private void buttonStop_Click(object sender, EventArgs e) //"stop" button
{
if (workerThread != null)
{
if (workerThread.IsAlive == true)
{
stopWork = true;
workerThread.Join();
}
}
}
/*.......... some more code ............*/
}
public class WorkerClass
{
MainForm parentForm=null;
/*......... some variables and code ........*/
public WorkerClass(MainForm parentForm)
{
this.parentForm=parentForm;
}
/* .............. some more code ...........*/
public void doWork()
{
/*.......... some variables and code ...........*/
for(int i=0;i<100000;i++)
{
// ** Here is the check to see if parentForm has set stopWork to true **
if(parentForm.stopWork==true)
break;
/*......... do some work in the loop ..........*/
}
}
/********* and more code .........*/
}
我想我可能知道问题所在。 问题在于子线程中的“doWork”方法试图在父表单中通过调用“workerThread.Join()”方法阻止父表单时访问“stopWork”变量。所以,我认为这是一个“僵局”问题。
我是否正确地发现问题?或者我错了,问题出在其他地方?
如果这确实是一个僵局,解决这个问题的可能解决方案是什么?
我做了一些谷歌搜索,发现了很多关于线程同步的资源以及如何避免死锁。但我无法理解如何将它们专门用于我的问题。
我非常感谢有关解决此问题的任何帮助或指导。
答案 0 :(得分:3)
是的,您编写的代码高度易受僵局攻击。 BackgroundWorker类特别容易导致这种死锁。
问题出在代码中,我们无法在您的代码段中看到,即WorkerClass。您肯定会以某种方式做一些影响UI的事情,这始终是首先考虑创建线程的主要原因。您可能使用Control.Invoke()在UI线程上运行一些代码并更新控件。也许还表示工作线程已完成,例如,将按钮的Enable属性设置为true。
那是死锁城市,这样的代码在UI线程空闲之前无法运行,回到泵送其消息循环。它永远不会在你的情况下闲置,它被卡在Thread.Join()中。工作线程无法完成,因为UI线程不会空闲,UI线程无法进入空闲状态,因为工作线程未完成。死锁。
BackgroundWorker也存在这个问题,除非UI线程处于空闲状态,否则RunWorkerCompleted事件无法运行。您需要做的是 not 阻止UI线程。说起来容易做起,BGW可以帮助你做到这一点,因为它在完成时会运行一个事件。您可以通过Thread.Join()调用在此代码中执行此事件。你的类中需要一个布尔标志来表示你处于'等待完成'状态。 This answer有相关代码。
答案 1 :(得分:2)
使用BackgroundWorker代替此任务。如果要停止任务的执行,请调用后台工作程序的CancelAsync
方法。
一般来说,如果您对多线程没有专家级的理解,那么滚动自己的线程代码(在任何平台上)都会导致灾难(即使这样,它仍然很危险)。