1

現在、スレッドセーフである必要のないクラスがありますが、将来的にはスレッドセーフなバージョンを作成する必要があるかもしれません。私の見方では、関連する関数をロックしてスレッドセーフにするか、仮想化して後で子孫クラスのオーバーライドでロックすることができます。つまり、今日は次のいずれかを実行できます。

public void DoStuff()
{
    lock (this.SyncRoot)
    {
        // Do stuff...
    }
}

または私はこれを行うことができます:

public virtual void DoStuff()
{
    // Do stuff...
}

今日、仕事をより速く終わらせるオプションはどれですか?

4

7 に答える 7

4

仮想関数呼び出しは、基本的に配列ルックアップと間接関数呼び出しです。仮想呼び出しがループで発生している場合、つまり、仮想関数呼び出しが同じインスタンスの同じ場所から数回呼び出されている場合、ほとんどの反復で、インライン化されていない通常の関数呼び出しよりも遅くはありません。最新の CPU 分岐予測子は、仮想関数呼び出しのターゲットを予測し、関数のアドレスのフェッチと並行して、このターゲットを投機的に実行します。

一方、ロックには常に、内部で少なくとも 1 つまたは 2 つのアトミック操作が含まれます。このような操作は、メモリ バリアを必要とするため、CPU パイプラインに大損害を与えることがほぼ保証されています。

于 2010-01-21T16:18:42.097 に答える
2

同期化するつもりでDoStuffある (そして、それが特定のサブクラスに対してであることを保証する) 場合は、それを作成せvirtual、メンバーを使用protected virtualして実際の作業を行う方がよいでしょう。

public void DoStuff()
{
    lock(this.SyncRoot)
    {
        InternalDoStuff();
    }
}

protected virtual void InternalDoStuff()
{
    // do stuff
}

これにより、現在のコードを ing しない オプションも提供されますlock(つまり、他のコードを使用せずDoStuffに呼び出すだけInternalDoStuffです) が、継承されたコードに触れることなく、後日それを差し込むことができます。

速度に関しては、lockステートメントの配置は何の効果もありません。

于 2010-01-21T16:03:54.600 に答える
2

2 つ目は、仮想呼び出しがかなり安価であるためです (ロックも追加の呼び出しであり、仮想呼び出し自体よりも高価になります)。

また、2 つ目は、必要なときに正確にロックを実装することをユーザーに任せます。

于 2010-01-21T16:02:01.890 に答える
1

VB SyncLockをテストしたところ、300倍近く遅くなりました。

于 2010-01-22T21:37:42.823 に答える
1

仮想通話はほぼ確実に高速になります。仮想呼び出しには、余分なレベルの間接化が含まれます。通常、ロックにはカーネル モードへの切り替えが伴います。控えめに見積もっても、少なくとも 100 倍遅くなります。

于 2010-01-21T16:03:54.303 に答える
1

二番目。今日、スレッド セーフが必要ない場合は、実行しないでください。SyncRoot将来的にメソッドをスレッドセーフにできるように、プロパティを公開します。ただし、メソッドがスレッドセーフではないことをドキュメントに明確に記載してください。

于 2010-01-21T16:04:16.633 に答える
1

Egads、参照が見つかりません。おそらく Joe Duffy の本にありますが、Lock は、別のスレッドがヒットし始めるまで操作を行わない可能性があります (つまり、遅延作成)。

また、この Lock はいずれにしてもカーネル モードにはならず、Interlocked### API に基づいています。

最後に、これらのトピックについて考えるのは楽しいことですが、最善の方法は、常に独自のコードの時間を計ることです。

于 2010-01-21T16:32:46.277 に答える