-1

EDIT2--> 一番下を見てください。 <--EDIT2

私は(少なくとも私にとっては)奇妙な行動に遭遇しました。

簡単な WinForms クラスと簡単なクラス (以下のコード) を作成してテストしました。

lock(m_lock)前のlock(m_lock)呼び出しが終了していない場合、最初の呼び出しは待機して1回入力し、2番目の呼び出しはロックの範囲を離れると常に考えていました。いいえ。

アクションの流れは次のとおりです。

  1. Class1オブジェクトを作成します。

  2. Start()メソッドを呼び出します。

  3. メソッドでロックされてDoSomething()いる間にメソッドを呼び出します。m_lockrun

出力は次のとおりです。

始める()

ロックを取得しようとしています

獲得したロック

ロック解除

ロックを取得しようとしています

獲得したロック

DoSomething() ロックを取得しようとしています

...ハング...

私は何が欠けているか、間違っていますか? 私はC#(C ++から来た)の初心者なので、C#にはいくつかの落とし穴があるかもしれません。

そして、まだハングアップしています...(この投稿を書き終えるまでに)

編集--> 現実の世界では、ロックを使用してserialPortの読み取り/書き込み/構成を保護します(非同期ではなく同期読み取り/書き込みを使用)。WaitOneそして、いくつかの内部呼び出しがあることを dbg で確認します。関連性があるかどうかはわかりません。 <--編集

例を次に示します。

using System;

namespace LockTester
{
    public class Class1
    {
        object m_lock = null;
        bool m_isRunning;
        System.Threading.Thread m_thread = null;
        public Class1()
        {
            Console.WriteLine("Class1 ctor");
            m_lock = new object();
            m_isRunning = false;
        }
    
        public void DoSomething(){
            Console.WriteLine("DoSomething() Trying to acquire lock");
            lock(m_lock){
                Console.WriteLine("DoSomething() Acquired lock");
            }
            Console.WriteLine("DoSomething() Released lock");
        }
    
        public void Start(){
            Console.WriteLine("start()");
            m_isRunning = true;
            if (m_thread == null){
                m_thread = new System.Threading.Thread(Run);
            }
            m_thread.Start();
        }
    
        public void Stop(){
            Console.WriteLine("stop()");
            m_isRunning = false;
        }
    
        private void Run(){
            while (m_isRunning){
                Console.WriteLine("Trying to acquire lock");
                lock(m_lock){
                    Console.WriteLine("Acquired lock");
                    System.Threading.Thread.Sleep(1000);
                }
                Console.WriteLine("Released lock");
                System.Threading.Thread.Sleep(1000);
            }
        }
    }
}

EDIT2:

わかりました、答えが見つかりました。それはもう1つの共通点にありました。

コンソール出力を TextBox にリダイレクトするソリューションをどこかで (SO おそらく) 見つけました (純粋にテスト上の理由から、コンソールに出力されるテスト対象オブジェクトの内部メッセージをキャプチャできる、GUI を使用した小さなテスト アプリケーション)。

コードは次のとおりです。

私のフォームのコンストラクターで次のように使用されます:

_writer = new TextBoxStreamWriter(textBox1, this);

Console.SetOut(_writer);

public class TextBoxStreamWriter : TextWriter
{
    TextBox _output = null;
    Form _form = null;
    object _lock = new object();
    
    delegate void SetTextCallback(string text);
    
    private void SetText(string text)
    {
      // InvokeRequired required compares the thread ID of the
      // calling thread to the thread ID of the creating thread.
      // If these threads are different, it returns true.
      if (_output.InvokeRequired)
      { 
        SetTextCallback d = new SetTextCallback(SetText);
        _form.Invoke(d, new object[] { text });
      }
      else
      {
          _output.AppendText(text);
      }
    }
    
    
    public TextBoxStreamWriter(TextBox output, Form form)
    {
        _output = output;
        _form = form;
    }

    public override void Write(char value)
    {
        lock (_lock)
        {
            base.Write(value);
            SetText(value.ToString());
        }
    }

    public override Encoding Encoding
    {
        get { return System.Text.Encoding.UTF8; }
    }
}

なぜこれがこの問題を引き起こしたのか、誰でも説明できますか?

4

1 に答える 1

2

Form.Invokeを呼び出すと、次のようになります。

コントロールの基になるウィンドウ ハンドルを所有するスレッドで、指定されたデリゲートを実行します。

これを行う方法は、メッセージを所有スレッドのメッセージ キューに投稿し、そのスレッドがメッセージを処理するのを待つことです。

そのため、呼び出さInvokeれたデリゲートが呼び出されるまで戻らないブロッキング呼び出しです。

コードがブロックされている理由として考えられるのは、メインの GUI スレッドが、外部プログラムの完了など、何か他のことが起こるのを既に待っていることです。

そのため、実際にはメッセージを処理していません。

これが理由である場合、ここでの解決策は、GUI スレッドのブロック部分を削除することです。外部プログラムが完了するのをじっと待つのではなく、完了するのを待つタスクをスピンアウトし、完了したときにメイン フォームで適切なイベントを発生させます。その間、メイン スレッドは自由にメッセージを処理したり、テキスト ボックスを更新したりできます。

これは、外部プログラムの起動がボタン クリックなどのイベントに応答して行われる場合、ユーザーがボタンを 2 回クリックすることを避けるために、プログラムの実行中にユーザー インターフェイスの一部を無効にする必要がある場合があることに注意してください。両方とも同じテキスト ボックスにレポートする 2 つの並列実行。

結論: マルチスレッド プログラミングは難しい!

于 2013-10-30T11:01:02.983 に答える