pthread_join() и замороженное выполнение

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

У меня была очень раздражающая утечка памяти, потому что основной поток завершил работу, не присоединившись к другим потокам (ошибка новичка), поэтому для ее решения я добавил инструкции pthread_join() для каждого потока, полагая, что они заканчивают свою работу, поскольку выполнение каждого потока находился внутри цикла while с глобальным условием для проверки, которое было изменено основным потоком перед завершением. К сожалению, даже при этом сейчас большинство выполнений программы зависает в ожидании завершения потока.

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

Теперь я выкладываю компилируемый код с ошибкой, вы можете скомпилировать его с помощью gcc -o sample -pthread sample.c -lm

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <math.h>

#define SIZE 100
#define READY 12345
#define END 99
#define TIME 3600
int *advicer;
int  *td;
pthread_t *phones;
sem_t *counter;
int ready;
int end;

void cycle(int cycles){
    int i,j,k;
    for(i = 0; i < cycles; i++){
        k = 1;
        while(k){
            k = 0;
            for(j = 0; j < SIZE; j++){
                if(advicer[j] == 0){
                    k = 1;
                    break;
                }
            }
        }
        for(j = 0; j < SIZE; j++){
            advicer[j] = 0;
            sem_post(&counter[j]);
        }
    }
}

void *do_something(void *td){   
    int t;
    t = *((int *) td);
    while(end != END){
        if(end == END)
            break;
        t += t;
        advicer[t] = 1;
        sem_wait(&counter[t]);      
    }
    pthread_exit(NULL);
}

void all_free(){
    int i,j;
    end = END;
    printf("reach %d\n",end);
    for(i = 0; i < SIZE; i++)
        sem_post(&counter[i]);
    printf("reach2\n");
    for(i = 0; i < SIZE; i++){
        pthread_join(phones[i],NULL);
    }
    free(phones);   
    printf("reach3\n");
    for(i = 0; i < SIZE; i++)
        sem_destroy(&counter[i]);
    free(counter);
    free(td);
    free(advicer);
}

void main(){
    int i,my_count;
    counter = (sem_t *)malloc(sizeof(sem_t)*SIZE);
    advicer = (int *)malloc(sizeof(int)*SIZE);
    td = (int *)malloc(sizeof(int)*SIZE);
    phones = (pthread_t *)malloc(sizeof(pthread_t)*SIZE);
    for(i = 0; i < SIZE; i++){
        sem_init(&counter[i], 0, 0);
        advicer[i] = 0;
    }
    ready = READY;
    my_count = 0;
    end = 0;
    for(i = 0; i < SIZE; i++){
        td[i] = i;
        pthread_create(&(phones[i]), NULL, do_something, (void *)(&td[i]));
    }   
    printf("starting simulation\n");
    while(my_count < TIME){
        cycle(60);
        printf("hello\n");
        my_count += 60;
    }
    printf("simulation ended\n");
    all_free();
}

person Mig    schedule 02.03.2012    source источник
comment
Код (насколько он близок к вашему фактическому коду) гораздо лучше передает то, что делает код, чем описание. Мелкие детали имеют большое значение в работе с нитками.   -  person Michael Burr    schedule 02.03.2012
comment
«У меня была очень раздражающая утечка памяти» — как вы узнали и на какой ОС вы работали? В большинстве ОС/crt выход из основного потока приводит к завершению процесса, что приводит к уничтожению всех потоков и освобождению всей памяти.   -  person Martin James    schedule 02.03.2012
comment
@MartinJames, потому что я использовал valgrind для поиска утечек памяти, и я обнаружил, что некоторые, кроме того, документы потоков posix, говорят, что если я не сделаю никакого соединения, ожидается, что произойдет соединение, потому что pthreads по умолчанию присоединяются, поэтому им нужно соединение сделать хорошо бесплатно. Кстати, ОС - Linux.   -  person Mig    schedule 03.03.2012
comment
@Mig - можно ли опубликовать компилируемый пример, демонстрирующий проблему? Похоже, что если вы закомментируете вызов do_something(), то не потребуется много времени, чтобы получить полный, компилируемый набор исходных кодов C. Я не вижу проблемы в опубликованном коде, так что это может быть что-то, что еще не показано (конечно, я могу просто упустить проблему, которая есть...)   -  person Michael Burr    schedule 04.03.2012
comment
@MichaelBurr, наконец, я смог опубликовать компилируемую версию проблемы.   -  person Mig    schedule 07.03.2012
comment
@Mig: когда я запускаю ваш обновленный пример, строка t += t; в do_something() вызывает ошибку, не связанную с тем, что pthread_join() никогда не возвращается (из-за ошибки «основной» цикл в main() никогда не заканчивается). Увеличение t таким образом приводит к тому, что потоки получают доступ за пределами конца массивов advicer[] и counter[]. Если я закомментирую эту строку, программа будет работать нормально в моих тестах - если вы закомментируете эту строку, вы все еще видите проблему?   -  person Michael Burr    schedule 08.03.2012


Ответы (1)


Следующий раздел может привести к блокировке:

for(i = 0; i < m->pp; i++)
  sem_post(&counter[i]);
for(i = 0; i < m->pp; i++){
 if(m->ppl[i] != NULL){
  phone_free(m->ppl[i]);
 }
 ...
}

Вы вызываете sem_post() только для одного потока (который разблокирует его), затем вызываете pthread_join() (через phone_free()) для всех потоков, что блокирует все, кроме того, для которого вы вызвали sem_post(), поскольку другие не завершаются, потому что застряли в sem_wait().

Хотя вы вызвали sem_post() во время основного выполнения (cycle()), значение могло быть получено потоком до вашего вызова кода завершения выполнения (map_free()) pthread_join() (через phone_free()).

Чтобы отменить поток, вы можете использовать pthread_cancel().

Чтобы избежать утечки памяти, когда вы покинули поток и не вызвали в нем pthread_join(), вы можете отсоединить поток, используя pthread_detach(). Затем ОС освобождает все ресурсы потока, когда поток завершается.

person alk    schedule 03.03.2012
comment
Вас одурачил плохо отформатированный код — в нем нет вложенного набора циклов. Вызов sem_post() является единственным оператором для первого цикла for, поэтому все семафоры «вывешиваются» перед переходом к циклу, который «присоединяется». - person Michael Burr; 04.03.2012
comment
@Michael Burr: Dxmn ... - ты прав. Во всяком случае, эта конструкция вводит состояние гонки, которое может привести к блокировке. Я исправлю свой ответ. Спасибо. - person alk; 04.03.2012
comment
Я тоже думаю, что обновление не подходит. Любой поток, который просыпается после этих вызовов sem_post(), увидит этот end == END и выйдет из цикла while. Поток должен перечитать переменную end, потому что sem_wait() является барьером памяти, а end является глобальным. Даже если рабочий поток завершится до того, как основной поток доберется до соответствующего pthread_join(), соединение завершится успешно. - person Michael Burr; 04.03.2012
comment
На самом деле, когда я нажал Enter для комментария выше, я понял, что не совсем уверен, что end является глобальным (поскольку у нас на самом деле нет объявления) - может быть, он объявлен как static в заголовке, поэтому каждый модуль получает собственную копию? Если да, это проблема. - person Michael Burr; 04.03.2012
comment
@Michael Burr: Да, кажется, если не совсем проснулся... простите. Поскольку end можно объявить static, я согласен. Также мы не знаем, что за что-то делает do_something(). - person alk; 04.03.2012