2

C#.NET アプリケーションのスレッド間の通信で問題に直面しています。誰かが可能な解決策について正しい方向に導いてくれることを願っています。

私は C#.NET でアプリケーションを持っています。これは Windows フォーム アプリケーションです。私のアプリケーションには 2 つのスレッドがあります。1 つのスレッドはメイン スレッド (UI スレッド) で、もう 1 つは子スレッドです。子スレッドを「workerThread」と呼びましょう アプリケーションで使用されるフォームは 1 つだけです。このフォームを「MainForm」と呼びましょう

MainForm が読み込まれると、子スレッドが開始されます (フォームの「Load」イベント ハンドラを使用してスレッドを開始します)。

MainForm クラスには、パブリック ブール変数である「stopWork」という名前の変数があり、子スレッドが引き続き動作するか停止するかを示すフラグとして機能します。

子スレッドで実行するメソッドを含む別のクラス (MainForm クラス以外) があります。この 2 番目のクラスを「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 .........*/
}

問題がどこにあるかを知ることができると思います。問題は、「workerThread.Join()」メソッドを呼び出して親フォームが既にブロックされているときに、親フォームの「stopWork」変数にアクセスしようとする子スレッドの「doWork」メソッドにあります。したがって、これは「デッドロック」の問題だと思います。

問題を特定するのは正しいですか?それとも私が間違っていて、問題は別の場所にありますか?

これが実際にデッドロックである場合、これを解決するための可能な解決策は何ですか?

少しグーグルで調べたところ、スレッドの同期とデッドロックを回避する方法に関する多くのリソースが見つかりました。しかし、私の問題に具体的に適用する方法がわかりませんでした。

この問題を解決するためのヘルプやガイダンスをいただければ幸いです。

4

2 に答える 2

3

はい、あなたが書いたコードはデッドロックに対して非常に脆弱です。BackgroundWorker クラスは、特にこの種のデッドロックを引き起こしやすい傾向があります。

問題は、スニペットである WorkerClass に表示されないコードにあります。なんらかの方法で UI に影響を与える何かを行っていることは間違いありません。最初にスレッドを作成することを検討する主な理由は常にあります。おそらく Control.Invoke() を使用して、UI スレッドでコードを実行し、コントロールを更新します。おそらく、ワーカー スレッドが完了したことを通知し、たとえば、ボタンの Enable プロパティを true に戻すためにも使用されます。

これはデッドロック シティです。そのようなコードは、UI スレッドがアイドル状態になり、メッセージ ループのポンピングに戻るまで実行できません。あなたの場合、アイドル状態になることはありません.Thread.Join()でスタックしています。UI スレッドがアイドル状態にならないため、ワーカー スレッドを完了できません。ワーカー スレッドが終了していないため、UI スレッドをアイドル状態にできません。デッドロック。

BackgroundWorker にもこの問題があります。UI スレッドがアイドル状態でない限り、RunWorkerCompleted イベントは実行できません。必要なことは、UI スレッドをブロックしないことです。言うは易く行うは難しですが、BGW は完了時にイベントを実行するため、これを正しく行うのに役立ちます。Thread.Join() 呼び出しの後のコードで現在行っていることはすべて、このイベントで行うことができます。「完了待ち」状態であることを示すために、クラスにブール値フラグが必要です。 この回答には関連するコードがあります。

于 2011-04-02T14:38:09.313 に答える