1

タスク番号を追跡するために、コードの一部をスレッドセーフにする必要があるマルチスレッドアプリケーションを作成しています。

私はこの方法を持っています:

private void IncrementTaskNumber() {
   Interlocked.Increment(ref _TaskNumber);
}

_TaskNumber は、同じクラスのプライベート int です。問題は、これにより「プロパティ、インデクサー、または動的メンバー アクセスが out または ref パラメーターとして渡されない可能性があります」という例外がスローされることです。これを回避するには、次のようにします。

private void IncrementTaskNumber() {
   int _taskNum = _TaskNumber;
   Interlocked.Increment(ref _taskNum);
   _TaskNumber = _taskNum;
}

これはまだスレッドセーフですか?

4

3 に答える 3

3

_TaskNumber は、同じクラスのプライベート int です。

_TaskNumberこれが機能するには、プライベートフィールドである必要があります。私有地として持っている可能性が高いです。

次のように定義します。

private int _TaskNumber;

そして、それはうまくいきます。

また、現在の回避策では競合状態が発生することに注意してください。一時変数を使用してアトミックインクリメントを効果的に取り除きます。これは、Interlockedそもそも使用の目的を無効にします。フィールドを直接インクリメントする必要があります。

于 2012-08-27T16:20:29.067 に答える
1

メソッドにロック機構がないようですIncrementTaskNumber。1 つの場所からのみ呼び出す場合を除き、スレッド セーフではありません。あなたがやりたかった最初の実装は、それがうまくいったとしても、最初の実装Interlocked.Increment(ref _TaskNumber);が完了する前に2回目に呼び出され、refパラメータに書き込まれた可能性があるため、どちらにもなりませんでした。

編集:スレッドセーフにしたい場合は、次のようにyoruメソッドを変更できます:

private void IncrementTaskNumber()
{
    lock (_TaskNumber)
        _TaskNumber++;
}

編集 2: (使用するlockのがアプリケーションにとってコストがかかりすぎる場合は、他のソリューションを検討することをお勧めします。)

于 2012-08-27T16:23:18.120 に答える
0

これは間違いなくスレッドセーフではありません:

private void IncrementTaskNumber() {
   int _taskNum = _TaskNumber;
   Interlocked.Increment(ref _taskNum);
   _TaskNumber = _taskNum;
}

シーケンスを実行しているスレッドは、これら 3 つの操作の間で中断される可能性があるためです。これは、その間にローカル値が変更された場合にインクリメントを再試行し続けるループを導入しない限り機能しませんが、それは基本的にInterlocked.Incrementを使用して再実装していることを意味しますInterlocked.Increment。:)

プロパティではなく、メンバー_TaskNumber変数を作成するだけです。

于 2012-08-27T16:42:57.183 に答える