LINUX.ORG.RU

Синхронизация потоков с помощью семафоров

 


0

1

В общем задача стоит в синхронизации 2х потоков: 1й поток ожидает данные из очереди (не занимая cpu), а 2ой их постоянно генерирует и пихает в очередь не дожидаясь, пока 1й их обработает. Решил использовать для этого семафоры. Можно ли функции семафоров sem_post и sem_wait использовать в разных потоках: sem_post вызвается только в 1ом треде, а sem_wait в 2м? Например (язык C):

//внутри 1ого потока
void *thread1_run(void* _a)
{
    ...
    for(;;) {
       //ждем события от другого треда
       sem_wait(&s);
       //дождались, выполняем код
       _data = popData();
       //тут данные обрабатываем, причем намного медленнее, чем они формируются
       ...
    }
    ...
}
//внутри 2ого потока
void *thread2_run(void *_b)
{
   ...
    for(;;) {
        //тут формируем данные
        ...
        pushData(data);
        sem_post(&s);
    }
    ...
}
Проверил этот вариант - работает, но пока нигде не встретил в интернете такое применение семафоров. Может вместо семафоров правильнее использовать что-то другое? Хочется это реализовать стандартными средствами pthreads без использования всяких poll и select.

можно, семафоры для этого и предназначены.

AptGet ★★★ ()

Если у тебя внутри popData и pushData не используется мутекс, охраняющий доступ к разделяемым данным, то будет жопа.

geekless ★★ ()
Ответ на: комментарий от geekless

ТС спрашивает, как реализовать очередь, блокирующая операцию pop(), если в очереди нет элементов.

Один из вариантов имплементации такого - семафор.

invy ★★★★★ ()
Последнее исправление: invy (всего исправлений: 2)
Ответ на: комментарий от invy

Это ЛОР, здесь отвечают не, что спрашиваешь, а то, что надо.

Он правильно реализовал очередь. А предупреждаю его о гонках, которые будут при доступе к разделяемому укзаателю, если он забыл воткнуть мутекс.

geekless ★★ ()

но пока нигде не встретил в интернете такое применение семафоров

Интересно почему? Может потому что превратил прогу в однопоточную? Нет, точно не поэтому...

anonymous ()

popData и pushData thread safe естественно. Кстати valgrind почему-то ругается на семафоры даже в коде из man sem_wait:

$valgrind -v --tool=drd --check-stack-var=yes --first-race-only=no --report- signal-unlocked=yes --show-confl-seg=yes --show-stack-usage=yes --trace-alloc=yes --trace-barrier=yes --trace-fork-join=yes --trace-hb=yes --trace-rwlock=yes --read-var-info=yes --trace-semaphore=yes --log-file=/tmp/grind.log ./tst 1 2

$cat /tmp/grind.log
....
==13965== Invalid semaphore: semaphore 0x601300
==13965==    at 0x4C34F5D: sem_timedwait (drd_pthread_intercepts.c:1061)
==13965==    by 0x400C11: main (in /home/user/projects/cpp/thread_queue_test/tst)
==13965== semaphore 0x601300 was first observed at:
==13965==    at 0x4C3342F: sem_init@* (drd_pthread_intercepts.c:960)
==13965==    by 0x400B0C: main (in /home/user/projects/cpp/thread_queue_test/tst)
....

ApostolPetr ()
Ответ на: комментарий от geekless

Я неточно выразился, имел в виду то, чем достигается корректность push/pop. data может быть lock-free структурой.

AptGet ★★★ ()

А как ты думаешь, для каких ещё целей предназначен семафор?

Не забудь про мутекс в popData() и в pushData(). Без него получишь эпический баг, и будешь месяц ковырять своё поделие в дебагере, но так ничего и не найдёшь.

У меня так знакомый умер.

nanoolinux ★★★★ ()
Ответ на: комментарий от Harald

для posix мутексов это в стандарте написано, тащемта

AptGet ★★★ ()

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

http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue

x_hash ()

Есть еще pthread condition variables. Возможно тебе подойдут.

dvl36 ()

А все уже существующие реализации очередей, конечно же, имеют «фатальный недостаток»?

rand ()
Ответ на: комментарий от rand

Проблема не в реализации очереди, а в том, чтобы эта очередь не потребляла CPU при ожидании данных. В качестве pushData popData можно использовать наверное почти любую thread safe реализацию.

ApostolPetr ()
Ответ на: комментарий от x_hash

Очорт... У нас это дело на проект впеарил лид вот в таком виде http://www.1024cores.net/home/in-russian/o-lock-free-algoritmah-bonus :) Очень забавно было: велосипедный кусок atomic, впиленный автором туда для совместимости с «MSVC x86-32» был неглядя утащен на... x64. Когда его выпилили и заменили на std::atomic, лид это аккуратно откатил и начал править велосипед под x64... Маневр никто не понял, спросили нафига, если есть std::atomic - и спросили, зачем ему вообще это надо (сама необходимость в проекте именно этой реализации очереди, с какого-то RSDNского междусобойчика, до сих пор под вопросом)... Внятного ответа не добились. Пиковую производительность связки с базой, где это предполагается использовать, пока никто не измерял.

slackwarrior ★★★★★ ()
Ответ на: комментарий от ApostolPetr

С чего бы ей потреблять CPU (если только там не spinlock)?
А вообще, Вам нужны мьютексы + condition variable.

rand ()

Меня смущало, что pthread_cond_signal из 1ого потока отловится во 2м только тогда, когда 2й находится в pthread_cond_wait. Поэтому я их не принимал во внимание. И зря (pushData и getData thread unsafe):

void *thread1_run(void *arg) {
...
    for(;;) {
        //формируем данные
        ...
        pthread_mutex_lock(&mutex);
        pushData(data);
        ++length;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
        ...
   }
...
}

void *thread2_run(void *arg) {
    for (;;) {
        pthread_mutex_lock(&mutex);
        while(length == 0) {
            pthread_cond_wait(&cond, &mutex);
        }
        _data = getData();
        --length;
        pthread_mutex_unlock(&mutex);
        //обрабатываем данные
        ...
    }
}

ApostolPetr ()

Проверил этот вариант - работает

Лень анализировать код, но баги с синхронизацией могут и не вылезти на тестировании.

но пока нигде не встретил в интернете такое применение семафоров.Может вместо семафоров правильнее использовать что-то другое?

Condition variables. То, что ты делаешь, называется «producer-consumer queue».

tailgunner ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.