1

次の例をコピーしましたMicrosoft Thread Example

以下のコードが得られますが、「this.progressBar1.Value = newval;」という行でエラーが発生します。「クロススレッド操作が無効です: コントロール 'progressBar1' は、それが作成されたスレッド以外のスレッドからアクセスされました。」

問題は何ですか?ありがとうダモ

C# コード

public partial class Form1 : Form
{         
    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        Thread trd = new Thread(new ThreadStart(this.ThreadTask));
        trd.IsBackground = true;
        trd.Start();
    }
    private void ThreadTask()
    {
        int stp;
        int newval;
        Random rnd = new Random();
        while (true)
        {
            stp = this.progressBar1.Step * rnd.Next(-1, 2);
            newval = this.progressBar1.Value + stp;
            if (newval > this.progressBar1.Maximum)
                newval = this.progressBar1.Maximum;
            else if (newval < this.progressBar1.Minimum)
                newval = this.progressBar1.Minimum;
            this.progressBar1.Value = newval;
            Thread.Sleep(100);
        }
    }
    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show("This is the main thread");
    }
}
4

4 に答える 4

2

まず、クラスを直接使用するのではなく、タスクなどの高レベルの手法を使用することを強く提案しました。Threadタスク クラスは使いやすいだけでなく、より効果的で、構成しやすく、最近直面したような問題を回避しやすくなっています。

非 UI スレッドから UI オブジェクトを更新しようとするコードの主な問題。UI テクノロジ (Windows フォームや WPF など) では、UI オブジェクトを作成するスレッドのみがそれらのプロパティにアクセスする必要があります。

これを修正するには、非 UI スレッドから UI スレッドに制御をマーシャリングする必要があります。そして、これを行うための多くのオプションがあります (ただし、それらはすべてSynchronizationContextと呼ばれる概念に関する構文糖衣に過ぎません):

  1. 同期コンテキストを直接使用:

.

// storing SynchronizationContext in the private field of your form
private SynchronizationContext _syncContext = SyncrhonizationContext.Current;

private void MethodFromTheSeparateThread()
{
  // Marshaling control to UI thread
  _syncContext.Post(d =>
            {
                // Put all your code that access UI elements here
            }, null);
}
  1. Gregor が既に述べたようにInvokeRequired/を使用しますInvoke

  2. TaskScheduler.FromSynchronizationContext を使用する

.

private void ImplementLongRunningOperation()
{
  int id;
  string name;
  Task.Factory.StartNew(() =>
  {
    // our long-runing part. Getting id and name
    id = 42;
    name = "Jonh Doe";
  }).ContinueWith(t =>
    {
       // Handling results from the previous task.
       // This callback would be called in UI thread!
       label1.Text = id.ToString();
       label2.Text = name;
    }, TaskScheduler.FromSynchronizationContext);
}

前述したように、 Tasks.NET 4.0 以降で作業している場合は、最後のアプローチ (を使用) が望ましい方法です。これにより、低レベルのクラスから解放されるだけでなく、データの取得と処理などの個別のステップを明確に分離できるため、より明確な設計につながります。

于 2012-11-29T21:13:58.183 に答える
1

新しいデリゲートを呼び出す必要があります。

    delegate void ThreadTaskDelegate();
    private void ThreadTask()
    {
        if (this.InvokeRequired)
        {
            ThreadTaskDelegate del = new ThreadTaskDelegate(ThreadTask);
            this.Invoke(del, null);
        }
        else
        {
            int stp;
            int newval;
            Random rnd = new Random();
            while (true)
            {
                stp = this.progressBar1.Step * rnd.Next(-1, 2);
                newval = this.progressBar1.Value + stp;

                if (newval > this.progressBar1.Maximum)
                    newval = this.progressBar1.Maximum;
                else if (newval < this.progressBar1.Minimum)
                    newval = this.progressBar1.Minimum;

                this.progressBar1.Value = newval;

                Thread.Sleep(100);
            }
        }


    }

ハッピーコーディング!:)

于 2012-11-29T21:03:02.850 に答える
1

例は貧弱です。コントロールが作成されたスレッドでコントロールにアクセスする必要があります。これはほとんどの場合、メインの UI スレッドです。(それぞれ独自のメッセージ ポンプを使用して、フォームごとに個別の UI スレッドを持つことができます。ただし、今は心配する必要はありません。)

コントロールにアクセスする前に、バックグラウンド スレッドはControl.Invoke(Delegate)を使用してメイン UI スレッドに変更する必要があります。そして、UI の作業が完了したら、できるだけ早く UI スレッドから抜け出します。

例えば:

private void ThreadTask()
{
    // This code runs in the background thread.
    while (true)
    {
        if (this.InvokeRequired)
        {
            // In order to access the UI controls, we must Invoke back to the UI thread
            this.Invoke(new ThreadStart(SetRandomProgress));
        }
        else
        {
            // We are already in the UI thread, so we don't have to Invoke
            SetRandomProgress();
        }

        // Wait briefly.  This wait happens in the background thread.
        // During this time, the UI is still responsive, because it is not blocked.
        // You can verify this by tweaking the duration to something longer (say, 5000 ms).
        Thread.Sleep(100);
    }
}

private void SetRandomProgress()
{
    Random rnd = new Random();
    int stp = this.progressBar1.Step * rnd.Next(-1, 2);
    int newval = this.progressBar1.Value + stp;
    if (newval > this.progressBar1.Maximum)
        newval = this.progressBar1.Maximum;
    else if (newval < this.progressBar1.Minimum)
        newval = this.progressBar1.Minimum;

    this.progressBar1.Value = newval;
}
于 2012-11-29T21:04:22.767 に答える
1

このようにコードを書き直すことができますprogressBar。UI スレッドで更新されますprogressBar。デリゲートを通じてアクセスするメソッドを呼び出します。コードを確認してください:

private void Form1_Load(object sender, EventArgs e)
        {
            Thread trd = new Thread(new ThreadStart(this.ThreadTask));
            trd.IsBackground = true;
            trd.Start();
        }
        private void ThreadTask()
        {
            Random rnd = new Random();
            while (true)
            {
                int randValue = rnd.Next(-1, 2);
                progressBar1.Invoke(new updater(UpdateProgressBar), new object[] {randValue});
                Thread.Sleep(100);
            }
        }
        private delegate void updater(int value);
        private void UpdateProgressBar(int randValue)
        {
            int stp = this.progressBar1.Step * randValue;
            int newval = this.progressBar1.Value + stp;
            if (newval > this.progressBar1.Maximum)
                newval = this.progressBar1.Maximum;
            else if (newval < this.progressBar1.Minimum)
                newval = this.progressBar1.Minimum;
            this.progressBar1.Value = newval;

        }
于 2012-11-29T21:17:48.553 に答える