Как прекратить поток, ожидающий операции семафора - PullRequest
3 голосов
/ 17 июля 2009

Я пишу программу, которая использует общую память и семафоры для ipc. Существует один основной процесс сервера, который создает общую память и семафоры. Любое количество клиентских процессов может подключаться к общей памяти и читать и записывать в нее, когда это разрешено. Семафоры предоставляют механизм блокировки для управления чтением и записью. Все работает нормально, кроме случаев, когда я пытаюсь завершить работу клиента. Блок семафоров для доступа к разделяемой памяти находится в потоке, и по завершении процесса у меня нет возможности освободить блок семафоров, поэтому поток завершается правильно. Как бы я пошел по этому поводу? Это для Linux.

Если быть точным, то есть один шм и два сема. Первый sem блокирует запись, а второй блокирует чтение. Когда клиенту есть что написать, он ожидает, что sem записи будет равен 0, затем устанавливает его в 1, пишет, затем устанавливает значение sem для чтения в 0, что освобождает ожидающий сервер, чтобы прочитать то, что написал клиент. после чтения сервер устанавливает запись в 0 и следующий клиент в строке получает возможность записи. Он зависает при вызове semop, который сбрасывается, когда read sem равен 0. Этот вызов semop находится в потоке, и мне нужно выяснить, как правильно выйти из этого потока, прежде чем позволить завершить основной поток.

Вот пример того, что я хочу сделать, но не работает (сон выдает себя за зависший вызов semop):

#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void termination_handler (int signum) {
    printf( "Got Signal\n" );
}

void *threadfunc( void *parm ) {
    struct sigaction action;

    action.sa_handler = termination_handler;
    sigemptyset( &action.sa_mask );
    action.sa_flags = 0;

    sigaction( SIGUSR1, &action, NULL );

    printf("Thread executing\n");

    sleep( 100 ); // pretending to be the semaphore

    pthread_exit( NULL );
}

int main() {
    int       status;
    pthread_t threadid;
    int       thread_stat;

    status = pthread_create( &threadid, NULL, threadfunc, NULL );

    if ( status <  0) {
        perror("pthread_create failed");
        exit(1);
    }

    sleep( 5 );

    status = pthread_kill( threadid, SIGUSR1 );

    if ( status <  0 )
        perror("pthread_kill failed");

    status = pthread_join( threadid, (void *)&thread_stat );
    if ( status <  0 )
        perror("pthread_join failed");

    exit( 0 );
}

Ответы [ 4 ]

4 голосов
/ 17 июля 2009

Он сказал, что это для Linux.

Было бы полезно, если бы вы могли точно сказать, как вы это делаете. Я предполагаю, что вы блокируете в sem_wait или sem_timedwait. Если ваш поток блокирует там и вы хотите прервать его, вы можете использовать pthread_kill.

pthread_kill(blocking_thread_id, SIGUSR1);

Конечно, вам нужно настроить правильный обработчик сигналов (man sigaction) для перехвата SIGUSR1, и вам нужно проверить код возврата sem_wait () для EINTR, и в этом случае вы можете делать все, что хотите, зная, что были прерваны и не получили блокировку.

В случае, если вы используете процессы, вы бы просто использовали kill (), а не pthread_kill (), предоставляя идентификатор процесса. (извините, изначально я неправильно прочитал и подумал, что вы используете темы)

1 голос
/ 20 июля 2009

У меня есть два с половиной ответа для вас. :)

Во-первых, ваш пример кода работает для меня (в Linux): pthread_kill успешно EINTR поднимает сон рабочего потока, как и ожидалось, примерно через пять секунд, как показано с несколькими printf с и запоминанием возвращаемого значения sleep . AFAICT, если вы хотите сигнализировать прерывание определенного потока, вы сделали это.

Во-вторых, попробуйте SEM_UNDO. Этот флаг может быть установлен в элементе sem_flg , переданном в аргументе sembuf semop , и, как следует из названия, отменит настройки семафора после завершения процесса. IIUC, когда вы убиваете клиента, этот клиент оставляет семафор неправильно заблокированным. SEM_UNDO было сделано именно для этого обстоятельства.

Наконец и с уважением, возможно, вы перевернули логику семафоров здесь? Когда я читаю ваш вопрос, нулевой полувывод означает «ресурс свободен», а полвальный единичный - «заблокирован ресурсом» (цитата: "... [клиент] ожидает, что значение записи равно 0, затем устанавливает это к 1, пишет ... "). Однако, если два или более пишущих клиента ждут, когда SysV sem упадет до нуля, они все будут освобождены вместе, когда это произойдет. Это довольно неприятное состояние гонки, которое может, по крайней мере, привести к неожиданным уменьшениям и приращениям семафора.

0 голосов
/ 06 августа 2013

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

Вы можете решить эту проблему, поместив запросы на чтение и запись в очередь (например, связанный список) и разрешив первому в очереди работать в защищенной области, и удалить его из списка, как только он попадет в область.

В случае операции «только для чтения» вы можете получить доступ к другим операциям чтения, входящим в защищенную область, а также при условии, что первая операция доступна только для чтения. Когда первой операцией является запись, защищенная область должна быть пустой, прежде чем разрешить ей доступ.

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

Когда потоки сидят в очереди, у каждого есть своя переменная условия, которую вы можете использовать для пробуждения любого из них. Условная переменная также должна быть защищена мьютексом. Вы можете сохранить условную переменную, мьютекс и т. Д. В структуре и поместить их в массив или список и сохранить идентификатор потока с каждым таким образом, чтобы было легко найти поток, который вы хотите активировать.

Когда нить просыпается, она сначала проверяет причину пробуждения. Если установлен флаг выхода, то поток знает, что нужно выйти.

0 голосов
/ 17 июля 2009

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

...