web-dev-qa-db-ja.com

C / C ++共有メモリで待機して通知する

Java C/C++では2つ以上のスレッド間の共有メモリの場合、待機して通知する方法は?pthreadライブラリを使用します。

33
Sajad Bahmani

待機/通知に使用するJavaオブジェクトの代わりに、ミューテックスと条件変数の2つのオブジェクトが必要です。これらはpthread_mutex_initpthread_cond_initで初期化されます。

Javaオブジェクトで同期する場合は、pthread_mutex_lockおよびpthread_mutex_unlockを使用します(Cでは、これらを手動でペアリングする必要があることに注意してください)。待機/通知する必要があります。ロック/ロック解除するだけで、条件変数は必要なくなり、ミューテックスだけが必要になります。ミューテックスは必ずしも「再帰的」ではないことに注意してください。つまり、すでにロックを保持している場合は、 initフラグを設定してその振る舞いが欲しいと言わない限り、それを再び受けることはできません。

Java.lang.Object.waitを呼び出す場合は、pthread_cond_waitまたはpthread_cond_timedwaitを呼び出します。

Java.lang.Object.notifyを呼び出す場合は、pthread_cond_signalを呼び出します。

Java.lang.Object.notifyAllを呼び出す場合は、pthread_cond_broadcastを呼び出します。

Javaの場合と同様に、待機関数から誤ったウェイクアップが発生する可能性があるため、シグナルを呼び出す前に設定され、待機後にチェックされる条件が必要です。また、ループでpthread_cond_waitを呼び出す必要があります。 Javaと同様に、待機中にmutexが解放されます。

モニターを保持しないとnotifyを呼び出せないJavaとは異なり、canはミューテックスを保持せずに実際にpthread_cond_signalを呼び出します。ただし、通常は何も得られず、多くの場合、非常に悪い考えです(通常、ロック-条件の設定-シグナル-ロックの解除が必要なため)。したがって、それを無視してJavaのように扱うのが最善です。

基本的なパターンはJavaと同じですが、偶然ではありません。ただし、これらのすべての関数のドキュメントを読んでください。さまざまなフラグやおもしろい振る舞いがあり、それらについて知ったり、回避したりする必要があるためです。

C++では、単にpthreads APIを使用するよりも少し良いことができます。少なくともミューテックスのロック/ロック解除にRAIIを適用する必要がありますが、使用できるC++ライブラリによっては、pthreads関数により多くのC++風のラッパーを使用した方がよい場合があります。

36
Steve Jessop

タイトルでは、CとC++をさりげなく「C/C++」にブレンドしています。私は、あなたがこの2つを組み合わせたプログラムを書いていないことを願っています。

C++ 11を使用している場合、移植可能で(C++であるため)pthreadに代わるはるかに安全で使いやすい(POSIXシステムでは、通常は内部でpthreadを使用します)。

待機/通知にはstd::condition_variable + std::mutexを使用できます。 この例 はその方法を示しています。

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex m;
std::condition_variable cv;
std::string data;
bool mainReady = false;
bool workerReader = false;

void worker_thread()
{
    // Wait until main() sends data
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return mainReady;});
    }

    std::cout << "Worker thread is processing data: " << data << std::endl;
    data += " after processing";

    // Send data back to main()
    {
        std::lock_guard<std::mutex> lk(m);
        workerReady = true;
        std::cout << "Worker thread signals data processing completed\n";
    }
    cv.notify_one();
}

int main()
{
    std::thread worker(worker_thread);

    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        mainReady = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();

    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return workerReady;});
    }
    std::cout << "Back in main(), data = " << data << '\n';


    // wait until worker dies finishes execution
    worker.join();
}

このコードは、C++がCよりも優れている他のいくつかの長所も強調しています。

  1. このコードには単一の生のポインタが含まれていません( これは危険です
  2. ラムダ式
  3. その他すべての種類 構文スワッグ
7
Domi

pthread_cond_waitおよびpthread_cond_signalを使用して、条件に基づいて同期することができます

6
jspcal

条件変数を使用することが1つの方法です。これらはLinuxでpthreadライブラリを使用するときに使用できます(リンクを参照)。

条件変数はタイプpthread_cond_tの変数であり、待機し、後でプロセスを継続するための適切な関数とともに使用されます。

4
jldupont

移植性を気にしない場合、Linuxはeventfdを提供します。これにより、必要なものが正確に提供されます。各eventfdは内部カウンターを保持します。デフォルトモードでは、カウンターがゼロの場合、eventfdからの読み取りはブロックされ、それ以外の場合は、すぐに戻ります。それに書き込むと、内部カウンタが増加します。

したがって、待機呼び出しは単にuint64_t buf_a; read(event_fd, &buf_a, sizeof(buf_a));となり、bufは8バイトのバッファーでなければなりません。待機中のスレッドに通知するには、uint64_t buf_b = 1; write(event_fd, &buf_b, sizeof(buf_b));を実行します。

2

利用可能な場合は、POSIXセマフォを使用できます。 pthreadライブラリーにはミューテックスがあり、これもうまくいくかもしれません。

1
user231967