2

これは私のコードです:

    Thread _th1, _th2, _th3, _th4;
int _i;

    void thMethod()
    {
        while(_i < 100){
            Thread.Sleep((new Random()).Next(1,500));
            Application.DoEvents();
            Console.WriteLine(_i);
            _i++;
        }
    }

    private void button4_Click(object sender, EventArgs e)
    {
        _th1 = new Thread(new ThreadStart(thMethod));
        _th1.Start();
        _th2 = new Thread(new ThreadStart(thMethod));
        _th2.Start();
        _th3 = new Thread(new ThreadStart(thMethod));
        _th3.Start();
        _th4 = new Thread(new ThreadStart(thMethod));
        _th4.Start();
    }

私がやりたいことは、マルチスレッドを使用して 0 から 99 まで Console.WriteLine を実行することであり、遅延はランダムです。

ただし、私のコードは重複した数字を出力します

さて、これが結果です。

0 1 2 2 4 5 6 7 8 9 10 10 12 13 14 14 16 16 18 19 20 20 22 22 24 24 26 26 28 28 30 31 32 33 34 35 36 36 38 39 40 40 42 44 86 49 50 50 52 52 54 54 54 56 58 58 58 60 60 62 62 62 62 62 65 66 68 68 68 68 70 70 72 72 74 74 76 76 78 78 78 80 82 82 84 84 86 87 88 88 88 90 90 90 92 92 94 94 96 96 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 98 100 101 102

ご覧のとおり、重複した数字が印刷されており、99 でさえ止まりません。

それで...このコードを正しく実行するために修正するにはどうすればよいですか?

====================== のように変更する必要がありますか..

    void thMethod()
    {
        while(_i < 100){
            Thread.Sleep((new Random()).Next(1,500));
            Application.DoEvents();
            Interlocked.Increment(ref _i);
            Console.WriteLine(_i);
        }
    }

これ?

4

6 に答える 6

3

Interlocked.Increment他の投稿で述べたように使用する必要がありますが。これはあなたが抱えている正確な問題ではありません。

数値が 100 を超える理由は、値を増やしている行が Sleep の後にあるためです。これにより、スレッドはループに入りますが、値を書き出すまでに、値はすでに 100 を超えるように変更されています。

たとえば、t1、t2、および t3 がループに入り、500 ミリ秒待機すると、i は 99 になります。その後、t4 が終了し、それを 100 にインクリメントします。t1 は 100 を出力し、t2 は 101 を出力し、t3 は 102 を出力します。

Interlocked.Increment は実際には問題を解決しません。これは、値が実際に書き出されるときに Thread.Sleep が引き続き不整合を引き起こすためです。

この問題を解決するに は、参照型ではないため _i をロックできないため、スレッド ロックを使用してロックする変数を宣言する必要があります。

object obj = new object();

void thMethod()
{
    while (_i < 100)
    {
        lock (obj)
        {
            Console.WriteLine(_i);
            _i++;
        }
        Thread.Sleep((new Random()).Next(1, 500));
    }
}

この方法では、重複がなくなり、値は 99 で止まります。

于 2012-07-30T22:11:55.080 に答える
1

マルチスレッド プログラムでは、複数のライターを持つ共有リソースには、何らかの同期メカニズムが必要です。

何が起こるかというと、そのうちの 1 つが変数をインクリメントする機会を得る前に、複数のスレッドが印刷のために変数にアクセスしているということrace conditionです。

これを解決するには、クリティカル セクションで変数をインクリメントする必要があります。

    void thMethod()
    {
        while (_i < 100)
        {
            Thread.Sleep((new Random()).Next(1, 500));
            Application.DoEvents();
            lock(this)
            {
                Console.WriteLine(_i);
                _i++;
            }
        }
    }

これは 100 で止まらないので、別の修正は次のようになります。

        while (_i < 100)
        {
            Thread.Sleep((new Random()).Next(1, 500));
            Application.DoEvents();
            lock(this)
            {
                if (_i >= 100) break;
                Console.WriteLine(_i);
                _i++;
            }
        }
于 2012-07-30T21:58:37.163 に答える
1

スレッドセーフな方法でInterlocked.Incrementインクリメントするために呼び出す必要があります。i

于 2012-07-30T21:56:12.363 に答える
0

変数 _i は、グローバル レベルではなく、thMethod で宣言する必要があります。

于 2012-07-30T21:59:05.190 に答える
0

調査されていない Monitor の使用以外に 2 つのオプションがあります

Interlocked メソッドのいずれかを使用して整数型をインクリメントするだけの場合は、最高のパフォーマンスが得られます。この変数を使用して完了を知らせることもできるのでInterlocked.CompareExchange、自然に適合します。この手法は、ロックフリーおよびウェイトフリーのソリューションで使用されます。

while (_i < 100)
{
    Thread.Sleep((new Random()).Next(1, 500));
    int initI;
    int modifiedI;
    bool valueUpdated = false;
    do
    {
        initI = _i;
        modifiedI = initI + 1;

        if (modifiedI > 100)
            break;

        valueUpdated =  (initI == Interlocked.CompareExchange(
                ref _i, modifiedI, initI) );

        if (valueUpdated)
            Console.WriteLine(modifiedI);

    } while (!valueUpdated)  ;


}

シンプルにする場合は、Parallel Class も使用できます。

Parallel.For(0, 100, i =>
    {
        Thread.Sleep((new Random()).Next(1, 500));
        _i++;
        Console.WriteLine("{0} | {1}" ,Thread.CurrentThread.ManagedThreadId, _i );
    }
);

デモンストレーションのために、ManagedThreadId の出力を追加したので、複数のスレッドが使用されていることがわかります。

スレッドの数に影響を与えるMaxDegreeOfParallelismセットを使用して ParallelOption を渡す代わりに、作成されるスレッドの数を直接制御できなくなることに注意してください。

于 2012-07-31T16:24:23.347 に答える