Подтвердить что ты не робот

Ждать и уведомлять в общей памяти C/С++

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

4b9b3361

Ответ 1

Вместо объекта 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, а не по совпадению. Однако прочитайте документацию обо всех этих функциях, потому что существуют различные флаги и забавные поведения, о которых вы хотите знать и/или избегать.

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

Ответ 2

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

Ответ 3

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

Если вы используете С++ 11, вы найдете портативный и (так как С++, так) гораздо более безопасный/простой в использовании вариант 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:

Ответ 4

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

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

Ответ 5

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

Ответ 6

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