Запуск нескольких pthreads с помощью pthread_cond_broadcast - PullRequest
0 голосов
/ 25 июня 2018

Так как примеры для pthreads с pthread_cond_broadcast wakeup редки, я написал один, но не уверен, правильно ли это синхронизировано и как это сделать:

  1. все потоки имеют одинаковый cи переменная mtx?
  2. необходимо ли при pthread_cond_wait вернуться к тесту, если какое-то условие действительно выполняется?в моем случае каждый вызов трансляции должен разбудить каждый поток ровно один раз, но никто другой не должен этого делать.(я предотвращаю ложные пробуждения?)
  3. программа в настоящее время не завершает работу, несмотря на тип асинхронной отмены.также не удалось выполнить отложенную отмену в примере кода, несмотря на то, что pthread_cond_wait является точкой отмены , поэтому .

в целом все работает так, как я ожидал.


 #include <pthread.h> 
    #include <iostream>
    #include <unistd.h>

    struct p_args{
        int who;
    };

    pthread_cond_t  c;      //share between compilation units
    pthread_mutex_t mtx;

    void *threadFunc(void *vargs){
        //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
        struct p_args * args = (struct p_args *) vargs;

        while(true){
            //wait for trigger one loop
            pthread_mutex_lock(&mtx);
            pthread_cond_wait(&c, &mtx);
            pthread_mutex_unlock(&mtx);
            //should be entangled output showing concurrent execution
            std::cout << "t " << args->who << std::endl;

            /* expensive work */
        }
        delete args;
    }

    int main(int argc, char* argv[])
    {
        pthread_cond_init(&c, NULL);
        pthread_mutex_init(&mtx, NULL);

        pthread_t thread_id[2];

        struct p_args *args0 = new p_args();
        struct p_args *args1 = new p_args();
        args0->who = 0;
        args1->who = 1;

        pthread_create(&thread_id[0], NULL,  threadFunc,    args0);
        pthread_create(&thread_id[1], NULL,  threadFunc,    args1);

        sleep(3);
        pthread_mutex_lock(&mtx);
        pthread_cond_broadcast(&c);
        pthread_mutex_unlock(&mtx);
        sleep(3);//test if thread waits  
        pthread_cancel(thread_id[0]);
        pthread_cancel(thread_id[1]);

        pthread_join (thread_id[0], NULL);
        pthread_join (thread_id[1], NULL);
        //could perform cleanup here
        return 0;
    }

Regarding exiting deferred:
thread_id[0] exits fine and i am stuck in line `pthread_join (thread_id[1], NULL);`, it says (Exiting) but seems stuck on a lock, with debugger:  
<br>
[![enter image description here][2]][2]
<br>

РЕДАКТИРОВАТЬ окончательное решение, которое я придумал:

#include <pthread.h>
#include <iostream>
#include <unistd.h>

struct p_args{
    int who;
};

pthread_cond_t  c;
pthread_mutex_t mtx;
bool doSome[2];
bool exitFlag;

void *threadFunc(void *vargs){
    struct p_args * args = (struct p_args *) vargs;

    while(true){
        //wait for trigger one loop
        pthread_mutex_lock(&mtx);
        do {
            pthread_cond_wait(&c, &mtx);
            if(exitFlag) {
                std::cout << "return " << args->who << std::endl;
                delete args;
                pthread_mutex_unlock(&mtx);
                return NULL;
            }
        } while(doSome == false);
        doSome[args->who] = false;
        pthread_mutex_unlock(&mtx);
        std::cout << "t " << args->who << std::endl;
    }
}

int main(int argc, char* argv[])
{
    pthread_cond_init(&c, NULL);
    pthread_mutex_init(&mtx, NULL);
    pthread_t thread_id[2];

    struct p_args *args0 = new p_args();
    struct p_args *args1 = new p_args();
    args0->who = 0;
    args1->who = 1;

    doSome[0] = doSome[1] = true;
    exitFlag = false;
    pthread_create(&thread_id[0], NULL,  threadFunc,    args0);
    pthread_create(&thread_id[1], NULL,  threadFunc,    args1);

    doSome[0] = doSome[1] = true;
    pthread_cond_broadcast(&c);
    sleep(3);
    doSome[0] = doSome[1] = true;
    pthread_cond_broadcast(&c);
    sleep(3);
    exitFlag = true;
    pthread_cond_broadcast(&c);

    pthread_join (thread_id[0], NULL);
    pthread_join (thread_id[1], NULL);

    return 0;
}

1 Ответ

0 голосов
/ 25 июня 2018
  1. все ли потоки используют одну и ту же переменную c и mtx?

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

  1. необходимо ли при возврате pthread_cond_wait проверить, действительно ли выполнено какое-либо условие?

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

  1. программа в настоящее время не завершает работу ...

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

Относительно выхода отложено: thread_id [0] завершается нормально, и я застрял в строке pthread_join (thread_id [1], NULL) ;, он говорит (Выход), но, кажется, застрял в блокировке

Одна из самых сложных вещей в pthread_cancel - это cleanup . Если отмена происходит во время удержания блокировки, вам нужно использовать pthread_cleanup_push для эмуляции совместимой с отменой семантики RAII. В противном случае первый поток может (и в этом случае умер) с заблокированным мьютексом.

В этом случае второй поток пытается выйти из pthread_const_wait из-за отмены, но он должен восстановить блокировку и не может.


Обычная форма цикла условной переменной такова (и хороший справочник должен показывать нечто подобное):

void *thread(void *data)
{
    struct Args *args = (struct Args *)data;

    /* this lock protects both the exit and work predicates.
     * It should probably be part of your argument struct,
     * globals are not recommended.
     * Error handling omitted for brevity,
     * but you should really check the return values.
     */
    pthread_mutex_lock(&args->mutex);

    while (!exit_predicate(args)) {
        while (!work_predicate(args)) {
            /* check the return value here too */
            pthread_cond_wait(&args->condition, &args->mutex);
        }
        /* work_predicate() is true and we have the lock */
        do_work(args);
    }

    /* unlock (explicitly) only once.
     * If you need to cope with cancellation, you do need
     * pthread_cleanup_push/pop instead.
     */
    pthread_mutex_unlock(&args->mutex);

    return data;
}

, где ваш код может просто войти в bool exit_predicate(struct Args*), bool work_predicate(struct Args*) и void do_work(struct Args*). Сама структура петли редко нуждается в значительных изменениях.

...