C#.NET Threading Question

▼魔方 西西 提交于 2021-02-04 19:51:07

问题


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.


回答1:


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.




回答2:


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).



来源:https://stackoverflow.com/questions/5522943/c-net-threading-question

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!