Control.InvokeとControl.BeginInvoke

5806 ワード

問題の導入

の下に簡単なdemoがあります.コードを見ると効果の例がわかります.winformのプログラムを新規作成し、次のコードを書きました.
using System;
using System.Windows.Forms;

namespace MyExampleList
{
    public partial class ControlInvokeExample : Form
    {
        public ControlInvokeExample()
        {
            InitializeComponent();
        }

        private void btnSayHello_Click(object sender, EventArgs e)
        {
            txtHello.Text = " C# ";
        }
    }
}
実行効果は以下の通りです:[caption id="attachment_1150"align="alignnone"width="364"]
表示テキスト[/caption]では、この簡単なdemoを改造します.プログラミングでマルチスレッドを使用するのはよくありますが、ボタンをクリックするときに別のスレッドを有効にし、textboxの値を操作して変化させます.コードは以下の通りです.
using System;
using System.Threading;
using System.Windows.Forms;

namespace MyExampleList
{
    public partial class ControlInvokeExample : Form
    {
        public ControlInvokeExample()
        {
            InitializeComponent();
        }

        private void btnSayHello_Click(object sender, EventArgs e)
        {
            Thread th=new Thread(new ThreadStart(StartMethord));
            th.Start();

        }

        private void StartMethord()
        {
            txtHello.Text = " C# ";
        }
    }
}
は理論的には上のコードで実行できるはずですが、実際にはエラーが報告されます.具体的なエラーは下図のようになります:[caption id="attachment_152"align="alignnone"width="860"]
スレッドインタラクションエラー[/caption]
エラー解釈:上記のエラーは、txtHelloというコントロールがthで作成されていないため、thスレッドが直接アクセスできないため、エラーを報告したことを意味します.
では、thスレッドでもUIのtxtHelloというtextboxの値を変えるにはどうすればいいのでしょうか.私たちはControlを通ります.InvokeまたはControl.BeginInvokeが実現します.

Control.InvokeとControl.BeginInvoke

Control.Invoke:このコントロールのベースウィンドウハンドルを持つスレッド上で指定した委任を実行します.Control.BeginInvoke:コントロールを作成するベースハンドルがあるスレッド上で、指定した委任を非同期で実行します.上記の説明に従って、上記のエラーコードを修正し、Invoke方式でthスレッド変更txtHelloの内容を実現します.具体的なコードは以下の通りです.
私は次のような実現方法をA)と呼んでいます.
using System;
using System.Threading;
using System.Windows.Forms;

namespace MyExampleList
{
    public partial class ControlInvokeExample : Form
    {
        public ControlInvokeExample()
        {
            InitializeComponent();
        }
        delegate void InvokeDelegate();
        private void btnSayHello_Click(object sender, EventArgs e)
        {
            Thread th=new Thread(new ThreadStart(StartMethord));
            th.Start();
        }
        private void StartMethord()
        {
            txtHello.Invoke(new InvokeDelegate(ChangeTextBox));
        }
        private void ChangeTextBox()
        {
            txtHello.Text = " C# ";
        }
    }
}
thスレッドがtxtHelloの内容を変更することをBeginInvoke方式で実現し、具体的なコードは以下の通りである.
私は次のような実現方法をB)と呼んでいます.
using System;
using System.Threading;
using System.Windows.Forms;

namespace MyExampleList
{
    public partial class ControlInvokeExample : Form
    {
        public ControlInvokeExample()
        {
            InitializeComponent();
        }
        delegate void InvokeDelegate();
        private void btnSayHello_Click(object sender, EventArgs e)
        {
            Thread th=new Thread(new ThreadStart(StartMethord));
            th.Start();
        }
        private void StartMethord()
        {
            txtHello.BeginInvoke(new InvokeDelegate(ChangeTextBox));
        }
        private void ChangeTextBox()
        {
            txtHello.Text = " C# ";
        }
    }
}
AとBの2つの実現方式を比較してみると、コード上の違いは、AがtxtHelloを使用していることだけだ.InvokeとB方式はtxtHelloを使った.BeginInvoke、つまり彼らの違いはまたInvokeとBeginInvokeに帰して、1つは同期で、1つは非同期で、つまりA方式の時はInvokeの実行結果を待って、B方式の時は直接実行してBeginInvokeの実行結果を待たない.本例のdemoでは方式AとBで実現する効果は同じであるが,原理が異なり,それだけである.次は新しいdemoを書き直して、この2つの方法の違いを見せてみましょう.変更されたインスタンスコードは次のとおりです.
using System;
using System.Threading;
using System.Windows.Forms;

namespace MyExampleList
{
    public partial class ControlInvokeExample : Form
    {
        public ControlInvokeExample()
        {
            InitializeComponent();
        }
        delegate void InvokeDelegate();
        private void btnSayHello_Click(object sender, EventArgs e)
        {
            Thread th=new Thread(new ThreadStart(StartMethord));
            th.Start();

        }
        private void StartMethord()
        {
            DateTime dt1 = DateTime.Now;
            txtHello.Invoke(new InvokeDelegate(ChangeTextBox));// ChangeTextBox , str 
            // txtHello.BeginInvoke(new InvokeDelegate(ChangeTextBox));// ChangeTextBox , str 
            string str=  (DateTime.Now - dt1).TotalMilliseconds.ToString();

        }
        private void ChangeTextBox()
        {
            Thread.Sleep(3000);
            txtHello.Text = " C# "+DateTime.Now.ToString();
        }
    }
}
実行後の結果は、[caption id="attachment_1154"align="alignnone"width="630"]の2つの図である.
invoke結果[/caption][caption id="attachment_155"align="alignnone"width="584"]
BeginInvokeの結果[/caption]は明らかに差があるでしょう.

では、いつInvokeとBeginInvokeを使用しなければなりませんか?

InvokeRequired:呼び出し元がコントロールをメソッド呼び出しするときにInvokeメソッドを呼び出す必要があるかどうかを示す値を取得します.呼び出し先は、コントロールが存在するスレッド(この例のUIスレッド)以外のスレッド(カスタムスレッドth)にあるためです.つまり、InvokeRequiredがtrueの場合、InvokeまたはBeginInvokeメソッドを使用してインタフェースを更新する必要があります. 

特に注意する

ControlのInvokeおよびBeginInvokeの委任方法は、メインスレッド、すなわちUIスレッド上で実行される.つまり、もしあなたの依頼方法が時間のかかるデータを取り、インタフェースを更新したりする場合は、UIスレッドでControlを呼び出さないでください.InvokeとControl.BeginInvokeは、UIスレッドを依然としてブロックしているため、インタフェースの偽死をもたらしている.では、この非同期はいったいどういう意味なのでしょうか.(具体的な解釈は上記AとBの方式を見ることができ、thスレッドの非同期を指す)非同期とは、UIスレッドに対して非同期ではなく、BeginInvokeを呼び出すスレッドに対して非同期であることを意味し、UIスレッド上でBeginInvokeを呼び出すのは当然だめである.--「InvokeとBeginInvokeの真の意味」から抜粋したコメント.BeginInvokeの原理は,呼び出された方法Marshalをメッセージにし,Win 32 APIのRegisterWindowMessage()を呼び出してUIウィンドウにメッセージを送信することである.−「InvokeとBeginInvokeの真の意味」から抜粋したコメント.