POSIX スレッド

最終更新: 2023-05-12 06:43:30 +0800 +0800
原文は英語で書かれています。あなたはAI翻訳版を読んでいます。

POSIX スレッドは、1つのプロセスと1つのアドレス空間内で並行実行を提供します。アドレス空間が共有されていても、共有可変状態が安全になるわけではありません。所有権とメモリ可視性は、ミューテックス、条件変数、または C のアトミック型によって確立する必要があります。

作成、終了、キャンセル #

pthread_create は、1つのポインター引数を受け取る void *(*start_routine)(void *) を開始します。エラーは errno を介さず、エラー番号として直接返します。スレッドは、開始ルーチンから戻る、pthread_exit を呼び出す、またはキャンセル要求に従うことで終了します。戻ることは、その戻り値を指定して pthread_exit を呼び出すことと同等です。exit を呼び出すか main から戻るとプロセス全体が終了しますが、初期スレッドで pthread_exit を呼び出した場合は、他のスレッドが実行を継続します。

join 可能なスレッドは、別のスレッドが pthread_join を正常に呼び出すまで、終了値とリソースを保持します。detach されたスレッドは終了時にそれらのリソースを解放するため、join できません。したがって、作成されたすべてのスレッドには、join または detach を担当する所有者を定義する必要があります。スレッドは自分自身を join してはならず、アプリケーションロジックによって、特定の対象を join するスレッドが1つだけになるよう保証する必要があります。

c
void *worker(void *arg) {
    return arg;
}

pthread_t thread;
int error = pthread_create(&thread, NULL, worker, value);
if (error == 0) {
    void *result;
    error = pthread_join(thread, &result);
}

キャンセルは、デフォルトで有効かつ遅延方式です。pthread_cancel はキャンセルを要求しますが、即時終了を保証するものではありません。条件待ちを含む、キャンセルポイントとして指定された呼び出しは、この要求に従って動作できます。キャンセルポイントをまたいでリソースを取得するコードでは、pthread_cleanup_push ハンドラーを登録するか、その他の方法でクリーンアップ規則を確立します。不変条件を操作するコードに非同期キャンセルを使用するのは適切ではありません。

属性とリソース上限 #

作成時にデフォルト以外の動作が必要な場合は pthread_attr_t を初期化し、pthread_create が戻った後に属性オブジェクトを破棄します。pthread_attr_setdetachstate は、join 可能または detach された状態での作成を選択します。pthread_attr_setstacksize、pthread_attr_setstack、および pthread_attr_setguardsize は、スタック割り当てとガードページを制御します。アプリケーションが提供するスタックは、スレッドが終了するまでマッピングされた状態を保ち、排他的に使用可能でなければなりません。

スケジューリング継承、ポリシー、優先度パラメーター、競合スコープも属性です。要求されたスケジューリングポリシーのサポートと、それを設定する権限は実装に依存します。pthread_attr_setaffinity_np のような Linux 固有のインターフェースは、プラットフォーム境界の背後に隔離してください。すべてのスレッドはスタック用アドレス空間とランタイムメタデータを消費するため、要求ごとに無制限にスレッドを作成する設計は、リソース枯渇を引き起こします。

ミューテックスと条件変数 #

条件変数は、ミューテックスによって保護された述語の変化を表すものであり、述語自体を表すものではありません。ミューテックスを保持している間に述語を検査し、ループ内で待機し、同じミューテックスを保持している間に述語を真または偽に変更し、その後でシグナルまたはブロードキャストを行います。

c
pthread_mutex_lock(&mutex);
while (!ready)
    pthread_cond_wait(&condition, &mutex);
consume();
pthread_mutex_unlock(&mutex);

pthread_cond_wait は、ミューテックスをアトミックに解放し、ミューテックスを取得して条件にシグナルを送る別のスレッドとの関係で待機を開始します。戻る前にミューテックスを再取得します。このアトミックなハンドオフにより、述語を検査してからブロックするまでの間に述語の更新が失われることを防ぎます。ウェイクアップは偽のものである可能性があり、またウェイクアップしたスレッドがミューテックスを再取得する前に別のコンシューマーが述語を変更する可能性があるため、ループは依然として必要です。

pthread_cond_timedwait は絶対期限を受け取ります。デフォルトのクロックは CLOCK_REALTIME です。実時間の調整がタイムアウトに影響しないようにする必要がある場合は、初期化前に条件属性のクロックを CLOCK_MONOTONIC に設定します。タイムアウトやキャンセルが発生しても、関数がミューテックスを再取得した後にそれを解放する義務はなくなりません。

ミューテックスのアンロックと、それに続く正常なロック取得は、保護された状態を公開するために必要な同期を確立します。条件へのシグナルはミューテックスの代わりにはならず、アトミックでない共有オブジェクトを同期なしで読み取ることはデータ競合のままです。アトミック型は、明示的に設計されたメモリ順序プロトコルがある場合にのみ使用してください。

プロセス共有オブジェクト #

PTHREAD_PROCESS_SHARED は、ミューテックスまたは条件変数をプロセス間で使用できるよう実装に要求します。オブジェクトとそれに関連する状態は、shm_open と MAP_SHARED の組み合わせなど、実際に共有されるマッピング上に存在する必要があります。仮想アドレスが同じである別々に割り当てられたメモリ上に置くだけでは不十分です。一部のオブジェクト型や環境ではプロセス共有が任意サポートであるため、属性と初期化のエラーを確認してください。どのスレッドまたはプロセスも使用する可能性がなくなるまで、ミューテックスや条件変数を破棄してはなりません。

POSIX 条件変数仕様と pthreads(7) では、API と Linux の動作が規定されています。

コメント

GitHub で閲覧・コメントする(投稿にはログインが必要です)