1

現在、54 ではなく 00 が出力されています。

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        Point[] array = new Point[20];
        public Form1()
        {
            for (int i = 0; i < 20; i++)
            {
                array[i] = new Point(); // I'm creating objects here
            }
            InitializeComponent();
        }

        void function1()
        {
            array[0].x = 5;
            array[0].y = 4;
        }

        void function2()
        {
            label1.Text = array[0].ToString();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread1 = new Thread(function1);
            thread1.Start();
            Thread.Sleep(500);
            function2();
        }
    }

    class Point
    {
        public int x;
        public int y;
        public override string ToString()
        {
            return x.ToString() + y.ToString();
        }
    }  
}

私がそのようにやっているとき(別のスレッドなしで)

private void Form1_Load(object sender, EventArgs e)
{
    function1();
    function2();
}

正常に動作し、出力は54です

4

4 に答える 4

2

実際、あなたのプログラムは私のコンピューターで期待どおりに動作しています。では、なぜあなたのコンピュータで動作しないのですか (または時々しか動作しないのですか)? これは、プログラムに競合状態があるためです。これは、プログラムが、(function1 を実行する) 2 番目のスレッドが 500 ミリ秒未満で配列に新しい値を割り当てると想定していることを意味します。他のスレッドがそのタスクを完了するのにかかる時間について、決して想定しないでください。オペレーティング システムは、個々のスレッドをいつ、どのくらいの頻度で、どのくらいの長さで実行し、いつ中断するかを自由に選択できます。代わりに、同期プリミティブ ( ManualResetEventなど) を使用して、2 番目のスレッドがそのタスクを完了したことを確認します。

このコードは、プログラムを変更してスレッド同期を適切に行う方法を示しています。

    ManualResetEvent MRE = new ManualResetEvent(false);//Add this field to Form1

    void function1()
    {
        array[0].x = 5;
        array[0].y = 4;
        MRE.Set();//This notifies first thread, that values are set.
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        Thread thread1 = new Thread(function1);
        thread1.Start();
        MRE.WaitOne();//This makes this thread wait until the second thread calls MRE.Set()
        function2();
    }
于 2012-04-19T11:07:30.520 に答える
2

あなたがしていることはあまり意味がありません。スレッドを開始し、500 ミリ秒スリープしてから、そのスレッドの結果を使用する関数を呼び出しています。

これは、2 つのアクションが順次依存しており、並行して実行できないことを意味します。ここでスレッドを使用しても意味がありません。

変更が表示されない理由は、おそらく、スリープが終了して呼び出されるthread1前にコードを開始して実行するのに十分な時間がないためです。function2代わりに使用してみてくださいthread1.Join()。しかし、私が言ったように、これはまったく意味がありません!

于 2012-04-19T10:28:15.307 に答える
1

function2の完了に依存している場合は、開始前に実際にfunction1完了していることを確認する必要があります。スレッドがどのようにスケジュールされ、実行されるかは予測できないため、そのジョブでの使用が常に機能するとは限りません。function1 function2Thread.Sleep

もう 1 つの問題Thread.Sleepは、UI スレッドをブロックしていることです。UI ベースのアプリケーションでマルチスレッドを使用するときの一番のルールは、UI スレッドを決してブロックしないことです。Thread.Sleepこれは、ManualResetEventThread.Join、 などを含むあらゆる種類のブロッキング呼び出しに適用されます。

この問題にアプローチする方法Taskは、メソッドでクラスを使用するContinueWithことです。これは、UI スレッドをブロックせずfunction1に呼び出す前に完了するまで待機します。function2

private void Form1_Load(object sender, EventArgs e)
{
  TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext();

  Task.Factory.StartNew(() =>
  {
    function1();
  }).ContinueWith((task =>
  {
    function2();
  }, ui);
}
于 2012-04-19T13:50:40.410 に答える
0

';' がないため、提供されたコードはコンパイルされません。Sleep(500) 呼び出しの後。

構文が修正されると、私のボックスで「機能」します。

終了したことを知らせる安全な方法がありません。あなたはそう学んでいます、最初のレッスン - sleep() 呼び出しでスレッドを待たないでください。

BackgroundWorker コンポーネントの方が使いやすいでしょう。結果を返すための明示的なシグナリングについて心配する必要はありません。

Thread インスタンスに固執する場合は、autoResetEvent クラスまたは Thread.Join() を参照してください。

ここに画像の説明を入力

于 2012-04-19T10:27:37.670 に答える