ждать и уведомлять в общей памяти C / C ++ - PullRequest
32 голосов
/ 18 января 2010

Как ждать и уведомлять, как в Java В C / C ++ для разделяемой памяти между двумя или более потоков? Я использую библиотеку pthread.

Ответы [ 6 ]

34 голосов
/ 18 января 2010

Вместо объекта Java, который вы будете использовать для ожидания / уведомления, вам понадобятся два объекта: мьютекс и переменная условия. Они инициализируются с pthread_mutex_init и pthread_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, мьютекс освобождается во время ожидания.

В отличие от Java, где вы не можете вызвать notify, если вы не держите монитор, вы можете фактически вызвать pthread_cond_signal, не удерживая мьютекс. Однако обычно он ничего не дает и часто является очень плохой идеей (потому что обычно вы хотите заблокировать - установить условие - сигнал - разблокировать). Поэтому лучше просто игнорировать это и относиться к нему как к Java.

На самом деле ничего особенного нет, основной шаблон такой же, как у Java, и не случайно. Тем не менее, прочитайте документацию по всем этим функциям, потому что есть различные флаги и забавные поведения, о которых вы хотите знать и / или избегать.

В C ++ вы можете сделать немного лучше, чем просто с помощью API pthreads. Вы должны по крайней мере применить RAII к блокировке / разблокировке мьютекса, но в зависимости от того, какие библиотеки C ++ вы можете использовать, вам может быть лучше использовать более обёртку C ++ - ish для функций pthreads.

7 голосов
/ 06 декабря 2013

В своем названии вы смешиваете C и C ++ вместе так случайно в "C / C ++". Надеюсь, вы не пишете программу, которая является смесью двух.

Если вы используете C ++ 11, вы найдете портативную и (потому что C ++, так) гораздо более безопасную / более простую в использовании альтернативу pthreads (хотя в системах POSIX она обычно использует pthreads внутри).

Вы можете использовать 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. все виды других синтаксических swagg .
6 голосов
/ 18 января 2010

pthread_cond_wait и pthread_cond_signal можно использовать для синхронизации на основе условия

4 голосов
/ 18 января 2010

Использование Условные переменные - это один из способов сделать это: они доступны при использовании библиотеки pthread в Linux (см. Ссылку).

Условная переменная является переменной Тип pthread_cond_t и используется с соответствующие функции для ожидания и позже, продолжение процесса.

2 голосов
/ 19 июня 2013

Если вас не волнует переносимость, 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));.

1 голос
/ 18 января 2010

Если доступно, вы можете использовать семафоры POSIX. В библиотеке pthread есть мьютексы, которые также могут работать на вас.

...