Задать вопрос

Как правильно прервать поток в Си?

Третьего дня начал изучать Си. Сразу же начал писать на нем.
Программа должна каждые 30 секунд посылать ~20-50 запросов к API ВКонтакте,
при том хотелось бы оставить себе право посылать команды из консоли, в том числе quit.
Реализацию накатил простую - вывел запросы в новый поток, который крутится пока
не изменится флаг. Однако в таком случае я таки использую глобальную переменную.
Сделал все примерно так:

#include <time.h>
#include <pthread.h>
#include <stdbool.h>
#include <string.h>

static bool quit = false;

static void* thread_process() {
    time_t last_cycle = 0;
    while(!quit) {
        if(difftime(time(NULL), last_cycle) > 30) {
            last_cycle = time(NULL);
            // Тут мои грязные дела
        }
    }
    pthread_exit(NULL);
    return NULL;
}

int main() {
    pthread_t th;
    pthread_create(&th, NULL, thread_process, NULL);

    char cmd[16];
    while(true) {
        scanf("%s", cmd);
        if(!strcmp(cmd, "quit")) {
            quit = true;
            break;
        }
        // остальные команды
    }

    pthread_join(th, NULL);
    pthread_exit(NULL);
    return 0;
}


Хотелось бы услышать мнение опытных разработчиков в данном вопросе. Может стоит вынести флаг в параметры потока, или есть какая-то конкретная функция для данных целей?
  • Вопрос задан
  • 526 просмотров
Подписаться 2 Простой Комментировать
Решения вопроса 1
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Общее правило при работе с потоками, не специфичное для С или pthreads: состояние резделяемое между потоками должно быть или атомарным или должно быть защищено примитивом синхронизации.
Код функции thread_process никак не гарантирует, что компилятор не выкинет проверку !quit, потому что quit не меняется в этой функции и в функциях вызываемых из неё.

Учитывая, что атомарность и потоки вошли в стандарт С11 имеет смысл смотреть туда.
Если С11 недоступен, смотреть в pthread_mutex_*, pthread_cond_*, ...

Может стоит вынести флаг в параметры потока

По-хорошему -- да, стоит. Это, однако, ортогонально к синхронизации доступа.

while(!quit) {
        if(difftime(time(NULL), last_cycle) > 30) {
            last_cycle = time(NULL);
            // Тут мои грязные дела
        }
    }

Вместо busy wait лучше использовать sleep или что-нибудь типа pthread_mutex_timedlock/pthread_cond_timedwait.

Я бы оформил код этого примера так:
#include <sys/time.h>
#include <pthread.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>

struct thread1 {
    pthread_mutex_t lock;
    pthread_cond_t cond;
    bool quit;
};

static void* thread_process(void *p) {
    struct thread1 *arg = p;

    for (;;) {
        bool quit;

        pthread_mutex_lock(&arg->lock);
        if (!arg->quit) {
            struct timeval now;
            struct timespec ts;

            gettimeofday(&now, NULL);
            ts.tv_sec = now.tv_sec + 30;
            ts.tv_nsec = now.tv_usec * 1000;
            pthread_cond_timedwait(&arg->cond, &arg->lock, &ts);
        }
        quit = arg->quit;
        pthread_mutex_unlock(&arg->lock);
        if (quit)
            return NULL;

        // Тут мои грязные дела
    }
    return NULL;
}

int main() {
    pthread_t th;
    struct thread1 arg = {
        .lock = PTHREAD_MUTEX_INITIALIZER,
        .cond = PTHREAD_COND_INITIALIZER,
        .quit = false,
    };
    pthread_create(&th, NULL, thread_process, &arg);

    char cmd[16];
    while(true) {
        scanf("%s", cmd);
        if(!strcmp(cmd, "quit")) {
            pthread_mutex_lock(&arg.lock);
            arg.quit = true;
            pthread_cond_broadcast(&arg.cond);
            pthread_mutex_unlock(&arg.lock);
            break;
        }
        // остальные команды
    }

    pthread_join(th, NULL);
    return 0;
}
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы