1

複数のスレッドからアクセスされるシングルトンが使用され、シングルトン自体がスレッドセーフである場合、シングルトンがアクセスされたときにどのスレッドがブロックされますか?

たとえば、メインスレッド A があるとします。A は最初にシングルトン S にアクセスし、次に別のことを行います。

少し後、スレッド B がシングルトン S にアクセスします。

B が S にアクセスする場合、シングルトンは引き続きスレッド A のコンテキストにあり、スレッド A をブロックしますか、それともスレッド B のみをブロックしますか (そして実際にアクセスしようとしている他のスレッドは?)

-> accesses
A->S {}
A->X {}
B->S {
...
C-S 
} - will B only block C or also A?

質問に答えるには: スレッドセーフなシングルトン (削除):

private static volatile Singleton instance;
    private static object _sync = new Object();

    private Singleton() {
        // dosomething...
    }

    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                lock (_sync)
                {
                    if (instance == null)
                        instance = new Singleton();
                }
            }

            return instance;
        }
    }

(およびメソッドでロックが発生する)

そして、質問は主に次の点に固有のものです。ロックにより、複数のスレッドがコードの同じ領域にアクセスできないことがわかっています。私の質問は、次の点に固有のものです。

シングルトンが生成されたスコープの元のスレッドがロックを保持していない場合、シングルトン インスタンスがスコープ内で作成されたため、別のスレッドがロックにアクセスするとブロックされますか? シングルトンは元のスレッドの範囲でのみ実行されますか?

4

3 に答える 3

0

スレッド セーフとは、通常、一度に 1 つのスレッドしかアクセスできないことを意味します。クリティカル セクションをロックすると、そのコードの一部を実行しようとする複数のスレッドがブロックされ、一度に 1 つのみが処理を進めてアクセスできるようになります。

あなたの質問で、クラスがクラスレベルで同期されていると仮定すると、A が S でメソッドを呼び出している間、同時にS を呼び出そうとする他のスレッドは、A が終了するまで待機する必要があります。

A が S の実行を完了すると、すべての待機中のスレッドを再スケジュールでき、そのうちの 1 つがロックを取得して S を実行します (残りの待機中のスレッドをブロックします)。

一方... A は、他の誰かが S にアクセスしている間に X を実行できます (同じロックを共有している場合を除く)。

ロック (特にこの例ではミューテックス) をトークンと考えてください。トークンを保持しているスレッドだけが、保護するコードを実行できます。それが完了すると、トークンがドロップされ、それを取得する次のスレッドが続行できます。

通常、同期は、特定のメソッドやメソッド内の特定のコード ブロックなど、クラス全体よりも細かいレベルで行われます。これにより、スレッドが互いに影響を与えない異なるメソッドに実際にアクセスできるときに、スレッドが待機する時間を無駄にすることを回避できます。

于 2012-12-18T13:49:39.817 に答える
0

通常、シングルトンのスレッド セーフは相互排除を意味します。つまり、スレッドがシングルトンを使用する必要がある場合、ロック/トークンを取得し、必要なことを行い、トークンを解放する必要があります。トークンを保持している間、他のスレッドはトークンを取得できません。それを試行するスレッドはブロックされ、FIFO キューに配置され、所有者がトークンを解放するとすぐにトークンを受け取ります。これにより、一度に 1 つのスレッドのみが保護されたリソース (この場合はシングルトン オブジェクト) にアクセスすることが保証されます。

これは典型的なシナリオです。あなたの走行距離は異なる場合があります。

関連して、 Singleton はほとんどの人から悪い考えと見なされています。

C# の同期メカニズムは、makc によってリンクされたチュートリアルのパート 2 でカバーされています。これは非常に優れています。

于 2012-12-18T13:47:11.600 に答える
0

シングルトンまたはその他のオブジェクトがどの程度スレッドセーフであるかによって異なります。

たとえば、Monitorまたはを使用するMutexと、これらのスレッド化同期アプローチのいずれかによって、保護されたコード ブロックにアクセスできるスレッドは 1 つだけになります。1 つのスレッドが同期されたコード ブロックに入ろうとし、別のスレッドがロックを取得したとします。2 番目のスレッドは、最初のスレッドがロックを解放するまで待機します。

一方、 を使用する場合Semaphoreは、保護されたコード ブロックを通過できるスレッドの数を定義します。Semaphoreが同時に 8 つのスレッドを許可するとします。可能性のある 9 番目のスレッドが保護されたコード ブロックに入ろうとするとSemaphore、キューに入れられたスレッドに使用できるスロットが 1 つ以上あることが通知されるまで待機します。

マルチスレッドを使用する場合、オブジェクトの同期に関してはさまざまな戦略があります。

この MSDN の記事を確認してください。

アップデート

更新された質問本文でコードを確認しました。

間違いなく:はい。ロックを取得したスレッドがロックを解除するまで、メイン スレッドを含むすべてのスレッドがブロックされます。

これがこのようにならないことを想像してみてください: 一部のスレッド ルールは、メイン スレッドを除くすべてのスレッドで機能します。同期されていないコード領域を持つチャンスです。

lockステートメントは、Monitor.EnterandのようなものにコンパイルされますMonitor.Exit。これは、ロックされたオブジェクトが現在のスレッドの排他ロックを取得することを意味します。

更新 2

一部のOPコメントから取得:

理由を説明できますか?つまり、メインスレッドが現時点でシングルトンで何もしない場合、スレッドはそのロックを取得しようとしないのですか?

おっと!スレッドの仕組みについて何か忘れているようです。

Monitor(lockキーワードは舞台裏で使用する) のようなスレッド同期アプローチを使用してコード領域を保護すると、ロックが解放されるまで作業中のスレッドをブロックするのMonitorではなく、保護/同期されたオブジェクトに入ろうとするすべてのスレッドをブロックします。Monitor

2 つのスレッドABがあり、次のコードがあるとします。

lock(_syncObject)
{
    // Do some stuff
}

スレッドAは同期されたコード領域を通過し、Bは保護された領域を通過しない他の処理を行っているバックグラウンド ワーカーです。この場合、Bはブロックされません

つまり、ある領域へのスレッド化されたアクセスを同期すると、オブジェクトが保護されます。lock(またはMutexAutoResetEventまたは何でも) は、仮想の のようなものと同等ではありませんThread.SleepAll()いずれかのスレッドが開始されて動作していて、誰も同期オブジェクト アクセスを行っていない場合、スレッドはブロックされません。

于 2012-12-18T13:55:02.807 に答える