0

ここにリストされているコードがあります: Threading and Sockets

その質問に対する答えは、 で変更することでしisListeningvolatile。私が指摘したように、その修飾子により、別のスレッドから変数にアクセスできました。MSDN を読んだ後、次の新しく作成されたスレッド プロセスから isListening を読んでいることに気付きました。

だから、今私の質問:

  • volatileは基本的に変数に対して非スレッドセーフなリクエストを行っているので、推奨される方法はありますか? Interlocked クラスについて読んだことがありますが、これが自分のコードで使用するのに適しているかどうか疑問に思いました。Interlocked は何をしているかに似ていますlock(myObj)が、もう少し「才能」とコントロールがあります。lock(myObj)コードブロックを適用するだけでisListeningはうまくいかないことはわかっています。

  • Interlocked クラスを実装する必要がありますか?

お時間をいただき、ご回答いただきありがとうございます。

4

3 に答える 3

2

C# で複数のスレッド間で変数の読み取りと書き込みを行うだけの場合、変数の型が bool、char、byte、sbyte、short、ushort、int であれば、その変数へのアクセスの同期 (ロック) について心配する必要はありません。、uint、float、および参照型。 詳しくはこちらをご覧ください。

他の投稿の例では、フィールドをそのままマークする必要がある理由は、volatileコンパイラの最適化の対象にならず、常に最新の値がフィールドに存在するようにするためです。キーワードの詳細については、こちらを参照してください。volatileこれを行うと、そのフィールドをロック (アクセスを同期) することなく、スレッド間で読み書きできるようになります。ただし、キーワードはbool 型であるためvolatileフィールドにしか使用できないことに注意してください。たとえば、double の場合、キーワードは機能せず、ロックを使用する必要があります。volatile

このInterlockedクラスは、特殊な目的、つまり (通常は) 数値型の値のインクリメント、デクリメント、および交換のために使用されます。これらの操作はアトミックではありません。たとえば、あるスレッドで値をインクリメントし、別のスレッドで結果の値を読み取ろうとする場合、通常は変数をロックして中間結果が読み取られないようにする必要があります。このInterlockedクラスはいくつかの便利な関数を提供するだけなので、インクリメント操作の実行中に変数を自分でロックする必要はありません。

フラグを使用して行っていることは、クラスisListeningを使用する必要はありません。Interlockedフィールドを揮発性としてマークするだけで十分です。

于 2009-11-18T14:24:20.337 に答える
1

ランチタイムの急いで答えのために編集します。

前のコードで使用されたlockステートメントは、メソッドのスコープで作成されたオブジェクトインスタンスをロックしているため、同じメソッドを呼び出す別のスレッドには影響しません。特定のコードブロックへのアクセスを同期するには、各スレッドがオブジェクトの同じインスタンスをロックできる必要があります。これを行う1つの方法(必要なセマンティクスに応じて)は、ロックオブジェクトを、それが使用されるクラスのプライベート静的変数にすることです。これにより、特定のオブジェクトの複数のインスタンスがコードブロックへのアクセスを同期できるようになります。単一共有リソース。インスタンス固有のオブジェクトまたはリソースの個々のインスタンスに同期が必要な場合は、静的を発行する必要があります。

Volatileは、指定された変数への読み取りまたは書き込みが異なるスレッド間でアトミックになることを保証しません。これは、命令の順序を保持し、変数がレジスタ内にキャッシュされないようにするためのコンパイラのヒントです。一般に、パフォーマンスに非常に敏感なもの(ローロック/ロックフリーアルゴリズム、データ構造など)に取り組んでいる場合、または実際に行っていることがわかっている場合を除いて、Interlockedを使用することを選択します。ほとんどのアプリケーションで揮発性/インターロック/ロックを使用する場合のパフォーマンスの違いはごくわずかであるため、最も安全な保証を提供するものを使用するのが最善かどうかわからない場合は(Joe Duffyのブログとを読んでください)。

たとえば、以下の例でvolatileを使用することはスレッドセーフではなく、インクリメントされたカウンターは10,000,000に達しません(テストを実行したときに8848450に達しました)。これは、volatileが最新の値を読み取ることのみを保証するためです(たとえば、レジ​​スタからキャッシュされないなど)。インターロックを使用する場合、操作はスレッドセーフであり、カウンターは10,000,000に達します。

public class Incrementor
{
    private volatile int count;

    public int Count
    {
        get { return count; }   
    }

    public void UnsafeIncrement()
    {
        count++;
    }

    public void SafeIncrement()
    {
        Interlocked.Increment(ref count);
    }
}

[TestFixture]
public class ThreadingTest
{
    private const int fiveMillion = 5000000;
    private const int tenMillion = 10000000;

    [Test]
    public void UnsafeCountShouldNotCountToTenMillion()
    {
        const int iterations = fiveMillion;
        Incrementor incrementor = new Incrementor();
        Thread thread1 = new Thread(() => UnsafeIncrement(incrementor, iterations));
        Thread thread2 = new Thread(() => UnsafeIncrement(incrementor, iterations));

        thread1.Start();
        thread2.Start();

        thread1.Join();
        thread2.Join();

        Assert.AreEqual(tenMillion, incrementor.Count);
    }

    [Test]
    public void SafeIncrementShouldCountToTenMillion()
    {
        const int iterations = fiveMillion;
        Incrementor incrementor = new Incrementor();
        Thread thread1 = new Thread(() => SafeIncrement(incrementor, iterations));
        Thread thread2 = new Thread(() => SafeIncrement(incrementor, iterations));

        thread1.Start();
        thread2.Start();

        thread1.Join();
        thread2.Join();

        Assert.AreEqual(tenMillion, incrementor.Count);
    }

    private void UnsafeIncrement(Incrementor incrementor, int times)
    {
        for (int i =0; i < times; ++i)
            incrementor.UnsafeIncrement();
    }

    private void SafeIncrement(Incrementor incrementor, int times)
    {
        for (int i = 0; i < times; ++i)
            incrementor.SafeIncrement();
    }
}

「interlockedvolatile」を検索すると、質問に対する多くの回答が見つかります。たとえば、以下の1つは、あなたの質問に対応しています。

以下の簡単な例は

揮発性vs.連動vs.ロック

于 2009-11-18T14:29:52.503 に答える
0

「これを行う1つの方法は、ロックオブジェクトを、それが使用されているクラスのプライベート静的変数にすることです。」なぜそれは静的でなければならないのですか?複数のスレッドが異なるオブジェクトで動作する限り、複数のスレッドから同じ関数にアクセスできます。機能しないと言っているわけではありませんが、アプリケーションの速度が大幅に低下するだけで、利点はありません。または、何か不足していますか?

MSDN が volatile について述べていることは次のとおりです。

揮発性オブジェクトへの書き込み (揮発性書き込み) にはリリース セマンティクスがあります。命令シーケンスで揮発性オブジェクトへの書き込みの前に発生するグローバルまたは静的オブジェクトへの参照は、コンパイルされたバイナリでの揮発性書き込みの前に発生します。

揮発性オブジェクトの読み取り (揮発性読み取り) には、取得セマンティクスがあります。命令シーケンスで揮発性メモリの読み取り後に発生するグローバルまたは静的オブジェクトへの参照は、コンパイルされたバイナリでの揮発性読み取りの後に発生します。

これにより、揮発性オブジェクトをマルチスレッド アプリケーションでのメモリのロックと解放に使用できます。」

于 2009-11-18T15:37:37.483 に答える