3

C#でチャットクライアント/サーバーアプリを作成していますが、スレッド化に問題があります。私は自分の問題を示すためにこの簡単なコードを書きました。

フォームを表示するためにthread_1を使用しましたが、1秒だけ表示されます(thread_1が終了してフォームを閉じた可能性がありますが、IsAliveは生きていると言っています!)。Thread_2は、メインスレッドで作成されたtexBoxに到達しようとしますが、次のように表示されます。

「クロススレッド操作が無効です:作成されたスレッド以外のスレッドからアクセスされたコントロール'textBox2'。」

最初の問題を解決する方法がわかりませんが、BackgroundWorkerで2番目の問題を解決しましたが、スレッドで解決するのが好きです。方法はありますか?

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    Thread t1;
    Thread t2;


    private void button1_Click(object sender, EventArgs e)
    {

        t1 = new Thread(doThread1);
        t1.Name = "thread_1";

        t2 = new Thread(doThread2);
        t2.Name = "thread_2";

        t1.Start();
        t2.Start();

        MessageBox.Show(t1.IsAlive.ToString());
    }

    private void doThread1()
    {
        Form frm2 = new Form();
        frm2.Show();
    }


    private void doThread2()
    {
        try
        {
            for (int j = 10000; j > 0; j--)
                textBox.Text = j.ToString();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }


}
4

1 に答える 1

2

Linkerroが述べたように、スレッド1と呼ばれるスレッドを削除する必要があります。これは、開始時にUIが既にスレッド内にあるためです(すべてのプログラムには、開始する単一のメインスレッドがあります)。ただし、あなたは正しい方向に進んでいましたが、UIをブロックしないように、実行時間の長いタスクを別のスレッドに配置したいと考えています。唯一の秘訣は、バックグラウンドスレッドからUIオブジェクトを直接操作できないことです。それらを所有するスレッドから操作する必要があります(これは、表示されるエラーメッセージです)。

幸い、.NETでこれを実現する非常に簡単な方法があります。WPFではUiComponent.Dispatcher.Invoke()を使用し、WinformsはUiComponent.Invoke()を使用するだけです。これにより、バックグラウンドスレッドは、UIコンポーネントが存在するスレッドにステップオーバーして更新できます。

Invokeは、UIスレッドで実行するアクションを表すデリゲートを取ります。私の例では、ランバダ式を使用して初期化され、パラメーターを受け取らず、値を返さないアクションを渡します。

これを試して

private void doThread2()
{
    try
    {
        for (int j = 10000; j > 0; j--)
        {
            textBox.Invoke(new Action(() =>
                textBox.Text = j.ToString()));
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

これは、タスクを使用してこれを行う方法の完全な例です。カウントしている間、ウィンドウを自由に動かすことができ、ロックされないことがわかります。ただし、タスクを取り出してループを終了すると、ループによってUIスレッドがブロックされるため、ウィンドウがどのようにフリーズするかがわかります。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Task.Factory.StartNew(() =>
        {
            for (int x = 0; x < 100000000; x++)
            {
                label1.Invoke(new Action(() =>
                    label1.Text = x.ToString()));
            }
        });
    }
}
于 2012-06-29T07:17:08.747 に答える