0

更新日1:

詳細: スレッド 1 と 2 は継続的にアクティブである必要があります。スレッド 1 は GUI を更新し、HTTP POST を実行しています。スレッド 2 は受信 HTTP POST に HTTPListener を使用し、そのデータをスレッド 1 に提供します。そのため、GUI は現在の Textbox 値で表示され、スレッド 2 がデータを提供するときに更新される必要があります。Servy または別のアプローチでは、両方のスレッドが同時に作業を行うことができますか? メインスレッドは、スレッド 2 が作業を完了するのを待っているようです。次に、prepWork を受け取り、それを処理します。Servy の例でコーディングしましたが、Task クラスで Run() の定義が見つかりませんでした。そのライブラリにはそのようなメソッドはありません。VS 2010 で Net 4.0 を使用しています。同等の使用方法はありますか? Start() もコンパイルされませんでした。Task は 1 回しか実行できないことを理解しています。

元の質問:

UI スレッド 1 として理解しているイベントでイベントが開始された場合、イベントを正常に開始し、イベント ハンドラーで GUI テキストボックスを更新するコードをテストしました。私の独立した Thread 2 メソッド PrepareDisplay(), Fire() が呼び出され、順番にイベントが発生します。スレッド セーフ呼び出しコード (WinForms のスレッド セーフに関する MSDN チュートリアルからモデル化) をいくつか入れましたが、イベント ハンドラーはテキスト ボックスを更新しません。コードをステップスルーすると、InvokeRequired が false のように見えます。私の最終的な目標は、スレッド 2 から UI スレッド 1 にデータを渡し、テキストボックスを新しいデータで更新することです。スレッドセーフ コードでこれが有効にならない理由がわかりません。誰かがこれをよりよく理解し、私が無視したことを理解するのを手伝ってもらえますか? 以下はコードです:

どうもありがとうございました、

namespace TstTxtBoxUpdate
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Aag_PrepDisplay aag_Prep1 = new Aag_PrepDisplay();

            Thread AagPrepDisplayThread = new Thread(new ThreadStart(aag_Prep1.PrepareDisplay));
            AagPrepDisplayThread.Start();

            while(!AagPrepDisplayThread.IsAlive)
                ;
            Thread.Sleep(1000);

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new SetOperation());
        }
    }
}

namespace TstTxtBoxUpdate
{
    // Thread 1: UI
    public partial class SetOperation : Form
    {
        private string text;
        public event Action<object> OnChDet;

        delegate void SetTextCallback(string text);
        private Thread demoThread = null;

        public SetOperation()
        {
            InitializeComponent();
            OnChDet += chDetDisplayHandler;
        }

        public void FireEvent(Aag_PrepDisplay aagPrep)
        {
            OnChDet(mName);
        }

        private void chDetDisplayHandler(object name)
        {
            this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe));
            this.demoThread.Start();
        }

        private void ThreadProcSafe()
        {
            this.SetText("402.5");
        }

        private void SetText(string text)
        {
            if(this.actFreqChan1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.actFreqChan1.Text = text;
            }
        }
    }
}

namespace TstTxtBoxUpdate
{
    // Thread 2: Data prepare
    public class Aag_PrepDisplay
    {
        #region Fields

        private Aag_PrepDisplay mAagPrep;

        #endregion Fields

        #region Properties

        public Aag_PrepDisplay AagPrepDisp;

        public Aag_PrepDisplay AagPrep
        {
            get { return mAagPrep; }
            set { mAagPrep = value; }
        }

        #endregion Properties

        #region Methods

        public void PrepareDisplay()
        {
            mAagPrep = new Aag_PrepDisplay();
            SetOperation setOp1 = new SetOperation();
            setOp1.FireEvent(mAagPrep);     // calls Thread 1 method that will fire the event
        }

        #endregion Methods
    }
}
4

1 に答える 1

2

InvokeRequiredメインスレッドがまだオンになっているときに呼び出すところまで来ていますThread.Sleep。まだメッセージ ループ ( の 1 つApplication.Run) を作成するところまで来ていないので、呼び出しをマーシャリングするためのメッセージ ループはありません。Invoke

ここにはあらゆる種類の問題があります。フォームの複数のインスタンス、表示するインスタンス、およびテキストを設定するまったく異なるフォームを作成しています。あなたは明らかにこれを行うつもりはありませんでした。テキストを設定する単一のフォームが必要です。

最初のスレッドが終了するまで、メイン スレッドで busywait を実行しないでください。まったく存在しないはずです。新しいスレッドがさらに別の新しいスレッドを作成しているという事実、2 番目のスレッドが終了し、2 番目のスレッドがメイン スレッドへの呼び出しをマーシャリングしようとするまでメイン スレッドがブロックされているという事実がなければ、通常はデッドロック。ここで 2 番目の新しいスレッドを作成するべきではありませんが、これは 2 つのバグが「互いに打ち消し合う」ケースです。これによりデッドロックは回避されますが、どちらも正しくないため、有効なソリューションにたどり着くことができなくなります。

Thread.Sleepまた、メイン スレッドに を含めるべきではありません。それが何を達成しようとしているのか、私にはわかりません。

最初のフォームを表示する前に長時間実行される作業を開始し、結果が得られたときにそのフォームを更新することが目標である場合、必要以上の作業を行っています

これを行うには、フォームのTaskコンストラクターで、長時間実行される作業の完了を表す を受け入れるようにします。そのタスクに継続を追加して、そのタスクの結果でラベルやテキストボックスを設定したり、何でもしたりできます。

public class SetOperation  : Form
{
    private Label label;
    public SetOperation(Task<string> prepWork)
    {
        prepWork.ContinueWith(t =>
        {
            label.Text = t.Result;
        }, TaskScheduler.FromCurrentSynchronizationContext());
    }
}

次に、メイン スレッドは単にTask、スレッド プール スレッドで指定された作業を行うために新しいスレッドを開始し、それをフォームに渡す必要があります。

[STAThread]
static void Main()
{
    Task<string> prepWork = Task.Run(() => DoWork());
    Application.Run(new SetOperation(prepWork));
}

private static string DoWork()
{
    Thread.Sleep(1000);//placeholder for real work
    return "hi";
}

これで完了です。DoWorkビジネスロジックを処理するために設計された独自のクラスにあるはずです。Programおそらくクラスに固執すべきではありません。

于 2014-02-03T21:41:49.180 に答える