9

C/C++ でのプリエンプティブマルチタスキング: 実行中のスレッドはタイマーによって中断され、タスクを切り替えることができますか?

グリーンスレッドなどを使用する多くの VM やその他の言語ランタイムは、これらの用語で実装されています。C/C++ アプリでも同じことができますか?

もしそうなら、どのように?

これはプラットフォームに依存するため、特定のプラットフォームでのサポートについて検討してください。SIGALRMたとえば、 Linux のハンドラーで (おそらく ? を使用して) ある種の内部スタックを交換するためにできる魔法があればlongjmp、それは素晴らしいことです!


気になったので質問します。

私は数年間、非同期 IO ループの作成に取り組んできました。非同期 IO ループを記述するときは、ループの DOS になるため、計算にコストのかかる計算をループに入れないように十分に注意する必要があります。

したがって、私は、非同期 IO ループを回復させたり、ある種のグリーン スレッドやそのようなアプローチを完全にサポートしたりするためのさまざまな方法に興味があります。たとえば、アクティブなタスクと a のループ反復回数をサンプリングし、SIGALRMタスクがブロックされていることが検出された場合は、他のすべてのものを新しいスレッドに移動するか、これを巧妙に変更して目的の結果を得ることができます。

最近、この点に関して node.js に関するいくつかの不満がありました。他の場所で、Go やHaskellなどの他のランタイムに関する興味をそそるコメントを見てきました。しかし、C/C++ のシングル スレッドでプリエンプティブマルチタスクを実行できるかどうかという基本的な問題から離れすぎないようにしましょう。

4

6 に答える 6

3

私が理解している限り、あなたは通常混合されていないものを混合しています:

  • 非同期
    シグナル シグナルは通常、現在実行中の同じスタック上のプログラム (したがって、説明では 1 つのスレッド) に配信され、登録されたシグナル ハンドラーを実行します... BSD unix では、ハンドラーを別のスタックで実行させるオプションがあります。いわゆる「シグナルスタック」。

  • スレッドとスタック
    独自のスタックでスレッドを実行するには、スタック領域を割り当て、状態情報 (すべてのレジスタを含む...) を保存および復元する機能が必要です。そうしないと、スレッド/プロセス間のクリーンな「コンテキスト スイッチ」などは不可能です。 . 通常、これはカーネルで実装され、非常に低レベルで非常に時間に敏感な操作であるため、何らかの形式のアセンブラーを使用することが非常によくあります。

  • スケジューラ
    私の知る限り、スレッドを実行できるすべてのシステムには、ある種のスケジューラがあります...これは基本的に、最高の特権で実行されるコードの一部です。多くの場合、何らかのハードウェア信号 (クロックなど) をサブスクライブし、他のコードがその同じ信号に直接 (間接的にのみ) 登録されないようにします。したがって、スケジューラには、そのシステム上のすべてのものを先取りする機能があります。主な問題は通常、利用可能なコアで十分な CPU サイクルをスレッドに与えてジョブを実行させることです。実装には、通常、ある種のキュー (複数の場合が多い)、優先度の処理、およびその他のいくつかの要素が含まれます。通常、カーネル側のスレッドは他のスレッドよりも優先度が高くなります。

  • 最新の CPU
    最新の CPU では、いくつかのコアやいくつかの「特別なスレッド」(ハイパースレッドなど) を処理する必要があるため、実装はかなり複雑です... 最新の CPU には通常、複数のレベルのキャッシュなどがあるため、これらを適切に処理することが非常に重要です。ハイパフォーマンスを実現するために。

上記のすべては、スレッドが OS によって定期的にプリエンプトされる可能性があり、おそらくプリエンプトされることを意味します。

Cでは、シグナルハンドラを登録して、同じスタック上のスレッドを先取りすることができます...再入力するとシグナルハンドラが問題になることに注意してください...処理をシグナルハンドラに入れるか、何らかの構造を埋めることができます(たとえば、キュ​​ー)そして、そのキューの内容をスレッドで消費させます...

setjmp/に関してlongjmpは、C++ で使用するといくつかの問題が発生しやすいことに注意する必要があります。

Linux では、カーネル スレッド (ディスク I/O...) よりも高い優先度でスレッドを実行するようスケジューラに指示できる「完全なプリエンプション パッチ」が利用可能です。

いくつかの参照については、

スケジューラなどの実際の実装を確認するには、 https: //kernel.org で Linux サービス コードをチェックしてください。

あなたの質問はあまり具体的ではないので、これが本当の答えかどうかはわかりませんが、始めるのに十分な情報があると思います.

述べる:

OSにすでに存在するものを実装したい理由がわかりません...一部の非同期I / Oでより高いパフォーマンスを実現する場合、通常、カーネルレベルで最大のパフォーマンスを利用できるオプションがいくつかあります(つまり、カーネルを記述します) -mode code)...おそらく、より具体的な回答が可能になるように明確にすることができます。

于 2011-10-30T10:27:54.953 に答える
3

Windows には、同じスレッドを共有する、ユーザーがスケジュールする実行単位であるファイバーがあります。 http://msdn.microsoft.com/en-us/library/windows/desktop/ms682661%28v=vs.85%29.aspx

UPD:ユーザーがスケジュールしたコンテキストの切り替えに関する詳細情報は、LuaJIT ソースで見つけることができます。これは、さまざまなプラットフォームのコルーチンをサポートしているため、lua をまったく使用していない場合でも、ソースを見ると役立ちます。概要は次のとおりです: http://coco.luajit.org/portability.html ,

于 2011-10-30T08:42:56.900 に答える
2

ユーザー空間のスレッド化ライブラリは通常、協調的です (例: GNU pth、SGI の statethreads など)。プリエンプティブ性が必要な場合は、カーネル レベルのスレッド化に進みます。

おそらくシグナルハンドラgetcontext()/setcontext()...から使用できますが、それが機能する場合、せいぜい面倒です。このアプローチが、カーネル スレッドやイベントベースの I/O よりも優れている点はわかりません。プリエンプティブ性のすべての非決定性が得られ、プログラムを順次制御フローに分割する必要がありません。SIGALARM

于 2011-10-30T10:14:29.453 に答える
1

他の人が概説したように、プリエンプティブを実行するのはおそらく簡単ではありません。

これの通常のパターンは、共同手順を使用することです。

コプロシージャーは、有限状態マシン (テキスト パーサー、通信ハンドラーなど) を表現するための非常に優れた方法です。

プリプロセッサ マクロ マジックを少し使用して、共同手順の構文を「エミュレート」できます。


最適入出力スケジューリングについて

Boost Asio: The Proactor Design Pattern: Concurrency Without Threadsを参照してください。

Asio には、単一 (IIRC) の単純なプリプロセッサ マクロに基づく共同手続きの「エミュレーション」モデルもあり、_stack のない共同手続きのコンパイラ サポートに不気味なほど近い、狡猾に設計されたテンプレート機能がいくつか組み合わされています。

サンプルのHTTP Server 4は、この手法の例です。

Boost Asio の作成者 (Kohlhoff) は、彼のブログでメカニズムとサンプルについて説明しています: A potted guide to stackless coroutines

このシリーズの他の記事もぜひご覧ください。

于 2011-10-30T10:15:54.410 に答える
0

あなたが求めていることは意味がありません。あなたの1つのスレッドは何によって中断されますか? 実行中のコードはスレッド内にある必要があります。そして、各スレッドは基本的にコードの順次実行です。スレッドが中断されるには、何かによって中断される必要があります。割り込みへの応答として、既存のスレッド内をランダムにジャンプすることはできません。そうすれば、通常の意味でのスレッドではなくなります。

あなたが通常行うことはこれです:

  • 複数のスレッドがあり、アラームがトリガーされるまでスレッドの 1 つが中断されているか、
  • または、ある種のイベント ループで実行される 1 つのスレッドがあり、そこで (他のソースの中でも) OS からイベントを受け取ります。アラームがトリガーされると、スレッドのイベント ループにメッセージが送信されます。スレッドが他の処理でビジー状態の場合、このメッセージはすぐには表示されませんが、イベント ループに戻ってイベントを処理すると、メッセージを取得して反応します。
于 2011-10-30T09:06:35.667 に答える
0

タイトルは矛盾した表現です。スレッドは独立した実行パスです。そのようなパスが 2 つある場合は、複数のスレッドがあります。

setjmp/longjmp を使用すると、一種の「貧乏人の」マルチタスクを行うことができますが、私はお勧めしません。

C も C++ も本質的にマルチスレッドをサポートしていませんが、ネイティブ Win32 スレッド、pthreads (POSIX スレッド)、boost スレッド、Qt や WxWidgets などのフレームワークなど、マルチスレッドをサポートする多数のライブラリがあり、スレッドもサポートしています。

于 2011-10-30T10:26:52.993 に答える