C#.NET 线程问题
我面临 C#.NET 应用程序中线程之间通信的问题。 希望有人能指导我找到可能的解决方案的正确方向。
我有一个 C#.NET 中的应用程序。它是一个 Windows 窗体应用程序。 我的应用程序有两个线程 - 一个线程是主线程(UI 线程),另一个线程是子线程。我们将子线程称为“workerThread” 应用程序中仅使用一种窗体。让我们将此窗体称为“MainForm”。
子线程在 MainForm 加载时启动(使用窗体的“Load”事件处理程序来启动线程)
在 MainForm 类中,我有一个变量名为“stopWork”,它是一个公共布尔变量,它充当一个标志来指示子线程是否应该继续工作或者应该停止
我有另一个类(除了 MainForm 类),其中包含我在子线程中执行的方法线。我们将第二个类称为“WorkerClass”。 我将对当前表单(MainForm)的引用传递到“WorkerClass”的构造函数中
我在主表单中有一个按钮“stop”,如果单击它,则将“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”方法试图访问父窗体中的“stopWork”变量,而父窗体已经通过调用“workerThread.Join()”方法被阻止。所以,我认为这是一个“僵局”问题。
我对问题的识别正确吗?或者我错了,问题出在其他地方?
如果这确实是一个僵局,有哪些可能的解决方案可以解决这个问题?
我做了一些谷歌搜索,发现了很多关于线程同步以及如何避免死锁的资源。但我不明白如何将它们专门应用于我的问题。
我非常感谢有关解决此问题的任何帮助或指导。
I am facing an issue with communication between threads in a C#.NET application.
Hope someone will guide me in the right direction about the possible solutions.
I have an application in C#.NET.It is a windows form application.
My application has two threads - One thread is the main thread (UI thread) and the other one is the child thread. Lets call the child thread the "workerThread"
There is only one form used in the application.Lets call this form the "MainForm"
The child thread is started when the MainForm loads (used the form's "Load" event handler to start the thread)
In the MainForm class, I have a variable named "stopWork" which is a public boolean variable and it serves as a flag to indicate whether the child thread should continue working or should it stop
I have another class (besides the MainForm class) which contains the method that I execute in the the child thread. Lets call this second class the "WorkerClass".
I pass a reference to the current form (the MainForm) into the constructor of the "WorkerClass"
I have a button "stop" in the main form which sets "stopWork" to "true" if its clicked and then calls "workerThread.Join()" to wait for the child thread to finish excecution.
In the child thread, the method "doWork" keeps checking the status of "parentForm.stopWork" inside a for loop. If "stopWork" is set to "true" then the loop breaks and subsequently the method ends.
Now, the issue is, once I am clicking the "stop" button ,the application hangs.
I am pasting parts of the code below so that it is easier to understand :
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 .........*/
}
I think I may know where the problem lies.
The problem is in the "doWork" method in the child thread trying to access "stopWork" variable in the parent form when already the parent form is blocked by calling the "workerThread.Join()" method. So ,I think this is a "deadlock" problem.
Am I right in identifying the problem ? Or am I wrong and the problem lies somewhere else ?
In case this is indeed a deadlock, what are the possible solutions to solve this ?
I did a bit of googling and found lots of resources on thread synchronisation and how to avoid deadlocks. But I could not understand how to apply them specifically to my problem.
I would really appreciate any help or guidance on resolving this issue.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
是的,您编写的代码非常容易出现死锁。 BackgroundWorker 类特别容易导致这种死锁。
问题出在我们在您的代码片段中看不到的代码中,即 WorkerClass。您肯定正在做一些以某种方式影响 UI 的事情,这始终是首先考虑创建线程的主要原因。您可能使用 Control.Invoke() 在 UI 线程上运行一些代码并更新控件。也许还可以发出工作线程已完成的信号,例如将按钮的 Enable 属性设置回 true。
这就是死锁城市,这样的代码无法运行,直到 UI 线程空闲,返回到消息循环。在你的情况下它永远不会空闲,它被困在 Thread.Join() 中。工作线程无法完成,因为 UI 线程不会空闲,UI 线程无法空闲,因为工作线程尚未完成。僵局。
BackgroundWorker也有这个问题,除非UI线程空闲,否则RunWorkerCompleted事件无法运行。您需要做的是不阻塞UI线程。说起来容易做起来难,BGW 可以帮助您正确完成此任务,因为它会在事件完成时运行该事件。您可以让此事件执行您现在在 Thread.Join() 调用之后的代码中执行的任何操作。您的类中需要一个布尔标志来指示您处于“等待完成”状态。 此答案有相关代码。
Yes, the code you wrote is highly vulnerable to deadlock. The BackgroundWorker class is especially prone to cause this kind of deadlock.
The problem is located in code we can't see in your snippet, the WorkerClass. You are surely doing something there that affects the UI in one way or another, always the primary reason to consider creating a thread in the first place. You probably use Control.Invoke() to have some code run on the UI thread and update a control. Perhaps also to signal that the worker thread is completed and, say, set the Enable property of a button back to true.
That's deadlock city, such code cannot run until the UI thread goes idle, back to pumping its message loop. It will never be idle in your case, it is stuck in Thread.Join(). The worker thread can't complete because the UI thread won't go idle, the UI thread can't go idle because the worker thread isn't finishing. Deadlock.
BackgroundWorker has this problem too, the RunWorkerCompleted event cannot run unless the UI thread is idle. What you need to do is not block the UI thread. Easier said than done, BGW can help you get this right because it runs an event when it completes. You can have this event do whatever you now do in the code past the Thread.Join() call. You'll need a boolean flag in your class to indicate that you are in the 'waiting for completion' state. This answer has relevant code.
请使用 BackgroundWorker 来执行此任务。当您想要停止任务的执行时,请调用后台工作线程的
CancelAsync
方法。一般来说,如果您对多线程没有专家级的理解,那么(在任何平台上)滚动您自己的线程代码都会导致灾难(即使如此,它仍然很危险)。
Use a BackgroundWorker for this task instead. When you want to stop the task's execution, call the background worker's
CancelAsync
method.Generally speaking, rolling your own threading code (on any platform) is a recipe for disaster if you don't have an expert-level understanding of multithreading (and even then it's still dangerous).