32

C++ API (Linux および Solaris 用) をスレッドセーフにしようとしています。これにより、内部データ構造を壊すことなく、その関数を異なるスレッドから呼び出すことができます。私の現在のアプローチでは、メンバー変数へのすべてのアクセスを保護するために pthread ミューテックスを使用しています。これは、単純なゲッター関数がミューテックスをロックおよびロック解除することを意味します。この API は主に、ミューテックス ロックが純粋なオーバーヘッドのように見えるシングル スレッド アプリで使用されるため、特にこのオーバーヘッドが心配です。

だから、私は尋ねたい:

  • ロックを使用するシングル スレッド アプリとロックを使用しないアプリのパフォーマンスを比較した経験はありますか?
  • これらのロック/ロック解除呼び出しは、たとえばと比較してどれくらい高価ですか。boolメンバー変数への単純な「return this->isActive」アクセス?
  • そのような変数アクセスを保護するより良い方法を知っていますか?
4

9 に答える 9

39

すべての最新のスレッド実装は、ユーザー空間で完全に競合のないミューテックス ロックを処理できます (2、3 のマシン命令のみで) - 競合がある場合にのみ、ライブラリはカーネルを呼び出す必要があります。

考慮すべきもう 1 つの点は、アプリケーションが pthread ライブラリに明示的にリンクしていない場合 (シングルスレッド アプリケーションであるため)、ダミーの pthread 関数 (ロックをまったく行わない) のみを取得することです。アプリケーションがマルチスレッド (および pthread ライブラリへのリンク) である場合、完全な pthread 関数が使用されます。

そして最後に、他の人がすでに指摘しているように、 isActive のようなものの getter メソッドをミューテックスで保護しても意味がありません - 呼び出し元が戻り値を見る機会を得ると、値はすでに変更されている可能性があります (ミューテックスは getter メソッド内でのみロックされます)。

于 2009-08-14T16:56:00.460 に答える
23

「mutex には OS コンテキスト スイッチが必要です。これはかなりコストがかかります。」

  • これは、mutex が futex と呼ばれるものを使用して実装されている Linux には当てはまりません。競合していない (つまり、まだロックされていない) ミューテックスの取得は、cmeerw が指摘するように、いくつかの単純な命令の問題であり、通常、現在のハードウェアで 25 ナノ秒の領域にあります。

詳細については: Futex

誰もが知っておくべき数字

于 2011-04-28T16:46:48.050 に答える
7

これは少し話題から外れていますが、あなたはスレッド化に慣れていないようです.1つには、スレッドがオーバーラップできる場所だけをロックしてください. 次に、それらの場所を最小限に抑えるようにしてください。また、すべてのメソッドをロックしようとするのではなく、スレッドがオブジェクトに対して (全体的に) 何を行っているかを考え、それを 1 回の呼び出しにしてロックします。ロックをできるだけ高くするようにしてください (これにより効率が向上し、デッドロックを回避するのに /役立つ可能性があります)。しかし、ロックは「構成」しません。スレッドがどこにあり、重複しているかによって、少なくとも精神的にコードをクロス編成する必要があります。

于 2009-08-14T16:45:39.620 に答える
4

私は同様のライブラリを作成しましたが、ロックのパフォーマンスに問題はありませんでした。(それらがどのように実装されているかを正確に伝えることはできないので、それが大したことではないと結論的に言うことはできません。)

私は最初にそれを正しくするために行きます(つまり、ロックを使用します)、それからパフォーマンスについて心配します。もっと良い方法はわかりません。それがミューテックスが構築された目的です。

シングルスレッドクライアントの代替手段は、プリプロセッサを使用して、ロックされていないバージョンとロックされたバージョンのライブラリを構築することです。例えば:

#ifdef BUILD_SINGLE_THREAD
    inline void lock () {}
    inline void unlock () {}
#else
    inline void lock () { doSomethingReal(); }
    inline void unlock () { doSomethingElseReal(); }
#endif

もちろん、シングルスレッドバージョンとマルチスレッドバージョンの両方を配布するため、維持するための追加のビルドが追加されます。

于 2009-08-14T12:44:58.977 に答える
3

Windows からわかるように、mutex はカーネル オブジェクトであり、(比較的) かなりのロック オーバーヘッドが発生します。スレッドで機能するロックだけが必要な場合、パフォーマンスの高いロックを取得するには、クリティカル セクションを使用する必要があります。これはプロセス間では機能せず、単一プロセス内のスレッドのみで機能します。

ただし.. Linuxは、マルチプロセスロックとはまったく異なる獣です。ミューテックスはアトミックCPU命令を使用して実装され、プロセスにのみ適用されることを知っています-したがって、それらはwin32クリティカルセクションと同じパフォーマンスを持ちます-つまり、非常に高速です。

もちろん、最速のロックは、まったくロックしないか、できるだけ使用しないことです (ただし、lib がスレッド化された環境で使用される場合は、できるだけ短時間ロックすることをお勧めします。ロックして、何かをして、ロックを解除して、何か他のことをしてから、もう一度ロックする方が、タスク全体でロックを保持するよりも優れています。別のスレッドが必要なロックを解放するために!)

于 2009-08-14T12:53:44.357 に答える
2

を使用する費用について知りたいと思いましたpthred_mutex_lock/unlock。ミューテックスを使用せずに1500〜65Kバイトのどこかにコピーするか、ミューテックスを使用して必要なデータへのポインターを1回書き込む必要があるシナリオがありました。

それぞれをテストするための短いループを作成しました

gettimeofday(&starttime, NULL)
COPY DATA
gettimeofday(&endtime, NULL)
timersub(&endtime, &starttime, &timediff)
print out timediff data

また

ettimeofday(&starttime, NULL)
pthread_mutex_lock(&mutex);
gettimeofday(&endtime, NULL)
pthread_mutex_unlock(&mutex);
timersub(&endtime, &starttime, &timediff)
print out timediff data

4000バイト程度未満をコピーしていた場合、ストレートコピー操作にかかる時間は短くなりました。ただし、4000バイトを超えるコピーを行っていた場合は、ミューテックスのロック/ロック解除を行う方がコストがかかりませんでした。

ミューテックスのロック/ロック解除のタイミングは、約2usecかかったcurrentTimeのgettimeofdayの時間を含めて、3〜5usecの長さで実行されました。

于 2010-08-07T16:14:42.727 に答える
2

ミューテックスにはOSコンテキストスイッチが必要です。それはかなり高価です。CPUは、それほど問題なく1秒間に数十万回実行できますが、ミューテックスがない場合よりもはるかに高価です。すべての変数アクセスにそれを置くことはおそらくやり過ぎです。

それはおそらくあなたが望むものではありません。この種のブルートフォースロックは、デッドロックにつながる傾向があります。

そのような可変アクセスを保護するためのより良い方法を知っていますか?

共有されるデータができるだけ少なくなるようにアプリケーションを設計してください。コードの一部のセクションは、おそらくミューテックスと同期する必要がありますが、実際に必要なセクションのみです。また、通常は個々の変数アクセスではなく、アトミックに実行する必要のある変数アクセスのグループを含むタスクです。is_active(おそらく、他のいくつかの変更とともにフラグを設定する必要があります。そのフラグを設定し、オブジェクトにそれ以上の変更を加えないことは意味がありますか?)

于 2009-08-14T12:44:19.430 に答える
1

メンバー変数アクセスの場合は、読み取り/書き込みロックを使用する必要があります。これにより、オーバーヘッドがわずかに少なくなり、ブロックせずに複数の同時読み取りが可能になります。

多くの場合、コンパイラがアトミックビルトインを提供していれば(gccまたはicc __sync_fetch *()などを使用している場合)、アトミックビルトインを使用できますが、正しく処理するのは非常に困難です。

アクセスがアトミックであることを保証できる場合(たとえば、x86では、dwordの読み取りまたは書き込みは常にアトミックであり、整列されているが、読み取り-変更-書き込みではない場合)、ロックをまったく回避し、代わりにvolatileを使用できますが、これは移植性がなく、ハードウェアの知識が必要です。

于 2009-08-14T16:39:04.610 に答える
0

最適ではありませんが簡単な方法は、ミューテックスのロックとロック解除の周りにマクロを配置することです。次に、スレッド化を有効/無効にするためのコンパイラ/メイクファイル オプションを用意します。

元。

#ifdef THREAD_ENABLED
#define pthread_mutex_lock(x) ... //actual mutex call
#endif

#ifndef THREAD_ENABLED
#define pthread_mutex_lock(x) ... //do nothing
#endif

次に、コンパイル時に aを実行しgcc -DTHREAD_ENABLEDてスレッド化を有効にします。

繰り返しますが、大規模なプロジェクトではこの方法を使用しません。ただし、かなり単純なものが必要な場合に限ります。

于 2009-08-14T20:43:26.677 に答える