6

スレッドを使用して、スレッドがビジーなときにプログラムがフリーズしないようにしようとしています。進行状況 (0 と 1 の書き込み) を表示し、完了後に結果を表示するだけでなく、その間にフォームをフリーズする必要があります。

現在のプログラムでは、テキストボックスに書き込もうとしていますが、実際には一定の進行状況が見られ、フォームは他のスレッドのタスクの影響を受けません。

私が今持っているのは、呼び出しを使用してスレッドでテキストボックスに書き込むことができるということですが、結果のみを表示し (スレッドがビジー状態のときにフォームがフリーズします)、フォームがフリーズします。

フォーム画像:

ここに画像の説明を入力

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

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

    Thread writeOne, writeTwo;

    private void writeText(TextBox textBox, string text)
    {
        if (textBox.InvokeRequired)
        {
            textBox.BeginInvoke((MethodInvoker)delegate()
            {
                for (int i = 0; i < 500; i++)
                {
                    textBox.Text += text;
                }
            });
        }
        else
        {
            for (int i = 0; i < 500; i++)
            {
                textBox.Text += text;
            }
        }
    }
    private void btnWrite1_Click(object sender, EventArgs e)
    {
        writeOne = new Thread(() => writeText(txtOutput1, "0"));
        writeOne.Start();
    }

    private void btnWrite2_Click(object sender, EventArgs e)
    {
        writeTwo = new Thread(() => writeText(txtOutput2, "1"));
        writeTwo.Start();
    }

    private void btnClear1_Click(object sender, EventArgs e)
    {
        txtOutput1.Clear();
    }

    private void btnClear2_Click(object sender, EventArgs e)
    {
        txtOutput2.Clear();
    }

    private void btnWriteBoth_Click(object sender, EventArgs e)
    {
        writeOne = new Thread(() => writeText(txtOutput1, "0"));
        writeTwo = new Thread(() => writeText(txtOutput2, "1"));

        writeOne.Start();
        writeTwo.Start();
    }

    private void btnClearBoth_Click(object sender, EventArgs e)
    {
        txtOutput1.Clear();
        txtOutput2.Clear();
    }
}

}

編集:

ところで、私はマルチスレッドが初めてで、これを行うための最良の方法を理解するために小さなプログラムを作成しようとしています。

フォームを更新する機会をまだ与えていなかったため、以前の呼び出しが実際には役に立たなかったことを理解しています。

このように 1 つのスレッドを実行しても機能しますが、複数のスレッドを一緒に実行すると、スレッドが完了するまでフォームが更新されません。
thread.sleep() を追加したので、書き込み中にクリアして、フォームをまだ使用できるかどうかを確認できます。

1つのテキストボックスに書き込むとき、書き込み中に画面をクリアできます。
しかし、2 つのスレッドを使用すると、スレッドが完了して出力が得られるまでフォームを使用できなくなります。

private void writeText(TextBox textBox, string text)
    {
        for (int i = 0; i < 500; i++)
        {
            Invoke(new MethodInvoker(() =>
            {
                textBox.Text += text;
                Thread.Sleep(2);
            }));
        }

    }

(私がこれについて完全に間違っている場合、いくつかの例/スレッドを読む必要はありません。バックグラウンドワーカー以外に、これを行うための最良の方法は何かをまだ確認しようとしています)

編集2:

書き込み量を減らすことで呼び出しの数を減らしましたが、遅延を増やして一定の書き込みと同じ効果を与え、負荷を減らすだけです。

private void writeText(TextBox textBox, string text)
    {
        for (int i = 0; i < 500; i++)
        {
            Invoke(new MethodInvoker(() =>
            {
                textBox.Text += text;
                Thread.Sleep(2);
            }));
        }

    }

編集3:

Sumeet の例は、

Application.DoEvents();

(s、.DoEventが機能しないことに注意してください。おそらくタイプミスです:P)、複数の文字列を同時に書き込み、結果だけでなく進行状況を表示します。

だからコードをもう一度更新してください:)

*新しいボタンを使用して、両方のテキスト ボックスに乱数を書き込む 5 つのスレッドを作成する

private void writeText(TextBox textBox, string text)
    {
        for (int i = 0; i < 57; i++)
        {
            Invoke(new MethodInvoker(() =>
            {
                textBox.Text += text;
                Thread.Sleep(5);
                Application.DoEvents();
            }));
        }

    }
private void btnNewThread_Click(object sender, EventArgs e)
    {
        Random random = new Random();
        int[] randomNumber = new int[5];
        for (int i = 0; i < 5; i++)
        {
            randomNumber[i] = random.Next(2, 9);
            new Thread(() => writeText(txtOutput1, randomNumber[i-1].ToString())).Start();
            new Thread(() => writeText(txtOutput2, randomNumber[i-1].ToString())).Start();
        }
    }
4

6 に答える 6

7

このソリューションは機能します!確認しました。

問題は、UIスレッドにテキストを変更するように指示し続けますが、更新されたテキストを表示する時間を与えないことです。UIに変更されたテキストを表示させるには、次のようにApplication.DoEvents行を追加します。

textBox.Text += text;
Application.DoEvents();

ps:If / Elseループのelseブロックを削除します。これは冗長であり、他の人が指摘しているように、UIスレッド自体にメッセージを投稿するだけなので、これら2つのスレッドを作成する必要はありません。

于 2013-01-28T19:40:47.277 に答える
5

必要に応じて UI スレッドで再起動するだけで、まだシングルスレッド タスクを実行しています。

for (int i = 0; i < 500; i++){
    string text = ""+i;
    textBox.BeginInvoke((MethodInvoker)delegate()
            {
                textBox.Text += text;
            });
}
于 2013-01-28T19:22:39.700 に答える
4

問題は、新しいスレッドを開始しようとしているのに、その新しいスレッドが、多くの作業を行う UI スレッドが処理する新しいタスクを 1 つ追加する以外に何もしていないことです。フォームの応答性を維持するには、UI スレッドが何もしない時間を持つか、少なくとも 1 つのタスクにかなりの時間を費やさないようにする必要があります。

フォームの応答性を維持するには、多くの小さなBeginInvoke(またはInvoke) 呼び出しが必要です。

private void writeText(TextBox textBox, string text)
{
    for (int i = 0; i < 500; i++)
    {
        Invoke(new MethodInvoker(() =>
        {
            textBox.Text += text;
        }));
    }
}

小さな呼び出し呼び出しを多数持つことで、ペイント イベント、マウスの移動/クリック イベントなどを操作の途中で処理できます。InvokeRequiredまた、通話を削除したことにも注意してください。このメソッドは非 UI スレッドから呼び出されることがわかっているため、その必要はありません。

于 2013-01-28T19:25:48.720 に答える
2

スレッドを使用する目的を無効にしています。

スレッドが行うことは、 UI スレッドに を使用してコードを実行するように指示することだけですBeginInvoke()

実際の作業はすべて UI スレッドで行われます。

于 2013-01-28T19:22:19.973 に答える
0

大量のアプリケーションでは文字列を使用しないでください。UIかどうか。マルチスレッドかどうか。

文字列を蓄積するには、StringBuilder を使用する必要があります。そして割り当てます

tb.Text = sb.ToString();
于 2013-01-31T04:01:02.983 に答える
0

データ処理を行っているか、UI をアニメーション化しようとしているだけです。

データ処理については、すべての重労働をバックグラウンド スレッドで実行し、UI を時々更新するだけにする必要があります。あなたの例では、基になるデータモデルに数百回データを追加していて、UI 要素 (TextBox) のレンダリングに毎回時間がかかるため、TextBox はこの点で特に厄介です。 UI 更新の処理によってデータ モデルの更新が圧倒されないように、UI を更新する頻度に注意する必要があります。TextBoxes はそのように厄介です。

次の例では、ペイント イベント中に設定されたフラグにより​​、TextBox が最後の更新の描画を完了するまで、追加の UI 更新がキューに入れられないようにします。

string str = string.Empty;
public void DoStuff()
{
    System.Threading.ThreadPool.QueueUserWorkItem(WorkerThread);
}

void WorkerThread(object unused)
{
    for (int i = 0; i < 1000; i++)
    {
        str += "0";
        if (updatedUI)
        {
            updatedUI = false;
            BeginInvoke(new Action<string>(UpdateUI), str);
        }
    }
    BeginInvoke(new Action<string>(UpdateUI), str);
}

private volatile bool updatedUI = true;
void textbox1_Paint(object sender, PaintEventArgs e) // event hooked up in Form constructor
{
    updatedUI = true;
}

void UpdateUI(string str)
{
    textBox1.Text = str;
}

一方、UI アニメーションが目的の場合は、おそらく TextBox 以外のものを使用する必要があります。それほど頻繁に更新を処理するようには設計されていません。特定のユース ケースに合わせてテキスト レンダリングを最適化できる場合があります。

于 2013-01-28T20:16:14.893 に答える