Java 9を学習中に onSpinWait
と呼ばれるThread
クラスの新しいメソッドに出会いました。javadocsによると、このメソッドはこのために使用されます:
他のアクティビティの一部で1つ以上のアクションが発生するまで、発信者が一時的に進行できないことを示します。
誰かがこの方法を理解して実際の例を教えてもらえますか?
これは、x86オペコードPAUSE
と同等(おそらくコンパイル)であり、Win32マクロYieldProcessor
、GCCの__mm_pause()
およびC#メソッド_Thread.SpinWait
_と同等です。
これは、非常に弱められた形式の譲歩です。何かが起こるのを待っている(ビジー待ち)多くのCPUサイクルを燃やす可能性があるループにいることをCPUに伝えます。
このようにして、CPUは実際にOSスケジューラーをロードし、すぐに実行可能なスレッド(高価になる可能性がある)をデキューすることなく、他のスレッドにより多くのリソースを割り当てることができます。
その一般的な用途はスピンロックです。共有メモリでの競合が非常にまれであるか、非常に速く終了することがわかっている場合、スピンロックは通常のロックよりも前もって実行できます。
そのような擬似コードは次のようになります。
_int state = 0; //1 - locked, 0 - unlocked
routine lock:
while state.cas(new_value=1, wanted_value=0) == false //if state is 0 (unlocked), store 1 (locked) and return true, otherwise just return false.
yield
routine unlock:
atomic_store(state,0)
_
yield
はThread.onSpinWait()
で実装できます。これは、ロックをロックしようとしている間、CPUが他のスレッドにより多くのリソースを提供できることを示唆しています。
ロックフリーアルゴリズムを実装する場合、ほとんどの場合アトミックな比較と交換ループとして実装されるビジー待機に依存するため、この降伏手法は非常に一般的で一般的です。これには、想像できるすべての実世界の用途があります。
純粋なシステムヒンティング!
この記事 引用:
目標
Javaコードがスピンループにあることをランタイムシステムに示唆するコードを許可するAPIを定義します。このAPIは純粋なヒントであり、セマンティックな動作要件は持ちません。たとえば、no-opは有効な実装です。特定のハードウェアプラットフォームで役立つ可能性のあるスピンループ固有の動作をJVMが利用できるようにします。少なくとも1つの主要なハードウェアプラットフォームでメリットがあります。
スコープ外の何かが変更されるまで、スレッドを中断する必要がある場合が多くあります。 (一度)一般的な方法は、1つのスレッドが他のスレッドが起動するのを待っているwait() notify()
パターンでした。
これには大きな制限があります。つまり、他のスレッドは待機スレッドが存在する可能性があることを認識し、通知する必要があります。他のスレッドの作業が制御できない場合、通知を受ける方法はありません。
唯一の方法はspin-waitです。新しいメールをチェックしてユーザーに通知するプログラムがあるとします:
while(true) {
while(!newEmailArrived()) {
}
makeNotification();
}
このコードは、1秒間に数百万回実行されます。貴重な電気とCPUパワーを使用して、何度も回転します。これを行う一般的な方法は、各反復で数秒待つことです。
while(true) {
while(!newEmailArrived()) {
try {
Thread.sleep(5000);
} catch(InterruptedException e) {
}
}
makeNotification();
}
これは非常に良い仕事をします。しかし、すぐに仕事をしなければならない場合、睡眠は問題にならないかもしれません。
Java 9は、この新しいメソッドを導入することでこの問題を解決しようとします。
while(true) {
while(!newEmailArrived()) {
Thread.onSpinWait();
}
makeNotification();
}
これは、メソッド呼び出しを行わない場合とまったく同じように機能しますが、システムはプロセスの優先順位を自由に下げることができます。他のより重要なことにリソースが必要な場合、このループのサイクルを遅くしたり、電力を削減したりします。
ドキュメントとそのソースコードを読んだ後、2セントを追加したいだけです。このメソッドmightはいくつかの最適化をトリガーしますが、そうしない場合があります-したがって、注意する必要があります-本当に頼ることはできません-これはhint
CPU
需要以上、おそらくいずれにせよ初期の依存はないでしょう...実際のソースコードは次のようになります:
@HotSpotIntrinsicCandidate
public static void onSpinWait() {}
これが実際に意味するのは、このメソッドは this に従って、JIT
のc2 compiler
に到達するまで基本的にNO-OPであることです。
実際の例として、何かをログに記録したいスレッドが、ログメッセージが「公開」される(ファイルに書き込まれるなど)のを待たずに非同期ログを実装したい場合、最終的にはそうなります(実際にやるべきことがあるからです)。
Producer(s):
concurrentQueue.Push("Log my message")
そして、実際にログメッセージをファイルに書き込むための専用のコンシューマスレッドを作成することに決めたとします。
(Single)Consumer
while (concurrentQueue.isEmpty())
{
//what should I do?
}
writeToFile(concurrentQueue.popHead());
//loop
問題は、whileブロック内で何をすべきかです。 Javaは理想的な解決策を提供していません。Thread.sleep()を実行できますが、どれくらいの時間とそれが重いのでしょうか。またはThread.yield()ですが、それは指定されていません。または、ロックまたはミューテックス*を使用することもできますが、多くの場合、非常に重くなり、プロデューサーの速度も低下します(そして非同期ロギングの目的を無効にします)。
あなたが本当に望むのは、「私はあまり長く待たないことを期待していますが、他のスレッドに対する待機/悪影響のオーバーヘッドを最小限に抑えたい」とランタイムに言うことです。そこでThread.onSpinWait()が登場します。
上記の応答が示すように、それをサポートするプラットフォーム(x86など)では、onSpinWait()がPAUSE命令に組み込まれます。これにより、必要な利点が得られます。そう:
(Single)Consumer
while (concurrentQueue.isEmpty())
{
Thread.onSpinWait();
}
writeToFile(concurrentQueue.popHead());
//loop
経験的に示されている これは、「ビジー待機」スタイルのループのレイテンシを改善できることです。
また、「スピンロック」の実装に役立つだけではないことを明確にしたいと思います(ただし、このような状況では間違いなく役立ちます)。上記のコードは、いかなる種類のロック(スピンまたはその他)を必要としません。
雑草に入りたいなら、 Intelの仕様 より良いことはできません
*わかりやすくするため、JVMはミューテックスのコストを最小限に抑えるために非常に賢く、最初は軽量ロックを使用しますが、それは別の議論です。