2015-08-19 3 views
2

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

Если у меня более одного потока в одном процессе. И я хочу сделать только один поток, чтобы спать при работе с другими потоками в рамках одного и того же процесса, есть ли какой-либо предопределенный синтаксис для него или я должен выполнить свою собственную реализацию (сон)?

В идеале я хочу отправить указание из потока в другой поток, когда пришло время для сна.

Отредактировано (2015-08-24) У меня есть два основных потока: один для отправки данных по сети, другой - данные из сети. Помимо джиттера, принимающий поток выполняет проверку и проверку и некоторое управление файлами, которое вовремя может привести к тому, что он перетащит за собой. Мне нравится делать что-то вроде микросота для отправителя, чтобы получатель мог догнать. sched_yield() не поможет в этом случае, потому что HW имеет многоядерный процессор с более чем 40 ядрами.

+0

Вам нужно будет выполнить свою собственную реализацию, но 'sleep (3)' обычно не то, что вы хотите в потоках, потому что оно может использовать сигналы ('SIGALRM'), и не гарантируется, что' SIGALRM' является доставляются на тот же конкретный поток (если другие потоки ждут какого-то сигнала, это может быть проблематично, и ваш поток может спать навсегда). Вам придется пойти на что-то более умное, например. 'select (2)' с FIFO или трубой. Вы можете уточнить свой вопрос? Включите распространенные случаи использования, почему вы хотите это сделать и т. Д. Это важно, чтобы мы могли выбрать правильный дизайн. –

+0

У меня есть два основных потока: один для отправки данных по сети, другой - данные из сети. Помимо джиттера, принимающий поток выполняет проверку и проверку и некоторое управление файлами, которое вовремя может привести к тому, что он перетащит за собой. Мне нравится делать что-то вроде микросота для отправителя, чтобы получатель мог догнать. sched_yield() не поможет в этом случае, потому что HW имеет многоядерный процессор с более чем 40 ядрами. –

+0

Я вижу. Это важно, вы должны отредактировать вопрос, чтобы включить эти данные. Я добавил ответ, который, я считаю, является тем, что вы ищете. –

ответ

2

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

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

Учитывая ваш прецедент и ограничения, я думаю, вам будет лучше использовать барьеры (см. pthread_barrier_init(3)). Предельные барьеры позволяют создать точку рандеву в коде, где потоки могут догнать.

Вы вызываете pthread_barrier_init(3) как часть кода инициализации, указав количество потоков, которые будут синхронизированы с использованием этого барьера. В этом случае это равно 2.

Затем потоки синхронизируются с другими, вызывая pthread_barrier_wait(3). Вызов блокирует до тех пор, пока количество потоков, указанных в pthread_barrier_init(3), не вызовет pthread_barrier_wait(3), после чего каждый поток, который был заблокирован в pthread_barrier_wait(3), будет запущен, и цикл начнется снова. По существу, барьеры создают точку синхронизации, где никто не может двигаться вперед, пока все не прибудут. Я думаю, это именно то, что вы ищете.

Вот пример, который имитирует быструю нить отправителя и поток медленного приемника. Они оба синхронизируются с барьерами, чтобы гарантировать, что отправитель не выполняет никакой работы, в то время как получатель все еще обрабатывает другие запросы. Нити синхронизируются в конце рабочего блока, но, конечно, вы можете выбрать, где каждый поток вызывает pthread_barrier_wait(3), тем самым точно контролируя синхронизацию потоков (и где).

#include <stdio.h> 
#include <pthread.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 

pthread_barrier_t barrier; 

void *sender_thr(void *arg) { 
    printf("Entered sender thread\n"); 

    int i; 
    for (i = 0; i < 10; i++) { 
     /* Simulate some work (500 ms) */ 
     if (usleep(500000) < 0) { 
      perror("usleep(3) error"); 
     } 

     printf("Sender thread synchronizing.\n"); 
     /* Wait for receiver to catch up */ 
     int barrier_res = pthread_barrier_wait(&barrier); 
     if (barrier_res == PTHREAD_BARRIER_SERIAL_THREAD) 
      printf("Sender thread was last.\n"); 
     else if (barrier_res == 0) 
      printf("Sender thread was first.\n"); 
     else 
      fprintf(stderr, "pthread_barrier_wait(3) error on sender: %s\n", strerror(barrier_res)); 
    } 

    return NULL; 
} 

void *receiver_thr(void *arg) { 
    printf("Entered receiver thread\n"); 

    int i; 
    for (i = 0; i < 10; i++) { 
     /* Simulate a lot of work */ 
     if (usleep(2000000) < 0) { 
      perror("usleep(3) error"); 
     } 

     printf("Receiver thread synchronizing.\n"); 
     /* Catch up with sender */ 
     int barrier_res = pthread_barrier_wait(&barrier); 
     if (barrier_res == PTHREAD_BARRIER_SERIAL_THREAD) 
      printf("Receiver thread was last.\n"); 
     else if (barrier_res == 0) 
      printf("Receiver thread was first.\n"); 
     else 
      fprintf(stderr, "pthread_barrier_wait(3) error on receiver: %s\n", strerror(barrier_res)); 
    } 

    return NULL; 
} 

int main(void) { 
    int barrier_res; 
    if ((barrier_res = pthread_barrier_init(&barrier, NULL, 2)) != 0) { 
     fprintf(stderr, "pthread_barrier_init(3) error: %s\n", strerror(barrier_res)); 
     exit(EXIT_FAILURE); 
    } 

    pthread_t threads[2]; 

    int thread_res; 
    if ((thread_res = pthread_create(&threads[0], NULL, sender_thr, NULL)) != 0) { 
     fprintf(stderr, "pthread_create(3) error on sender thread: %s\n", strerror(thread_res)); 
     exit(EXIT_FAILURE); 
    } 
    if ((thread_res = pthread_create(&threads[1], NULL, receiver_thr, NULL)) != 0) { 
     fprintf(stderr, "pthread_create(3) error on receiver thread: %s\n", strerror(thread_res)); 
     exit(EXIT_FAILURE); 
    } 

    /* Do some work... */ 

    if ((thread_res = pthread_join(threads[0], NULL)) != 0) { 
     fprintf(stderr, "pthread_join(3) error on sender thread: %s\n", strerror(thread_res)); 
     exit(EXIT_FAILURE); 
    } 
    if ((thread_res = pthread_join(threads[1], NULL)) != 0) { 
     fprintf(stderr, "pthread_join(3) error on receiver thread: %s\n", strerror(thread_res)); 
     exit(EXIT_FAILURE); 
    } 

    if ((barrier_res = pthread_barrier_destroy(&barrier)) != 0) { 
     fprintf(stderr, "pthread_barrier_destroy(3) error: %s\n", strerror(barrier_res)); 
     exit(EXIT_FAILURE); 
    } 

    return 0; 
} 

Обратите внимание, что, как указано в страницах руководства для pthread_barrier_wait(3) после того, как требуемое количество потоков назвать pthread_barrier_wait(3), состояние барьера сбрасывается в исходное состояние, которое было в использовании после последнего вызова pthread_barrier_init(3), что означает, что барьер атомарно разблокирует и сбрасывает состояние, поэтому он всегда готов к следующей точке синхронизации, что прекрасно.

Как только вы закончите с барьером, не забудьте освободить связанные ресурсы с помощью pthread_barrier_destroy(3).

+0

Спасибо за образец. У меня есть вопрос. В этом примере барьер инициируется для обработки двух потоков. Что делать, если один из потоков не вызывает wait_barrier, завершится ли процесс в тупике? –

+0

@ Zäta Другой поток будет ждать вечно. 'pthread_barrier_wait (3)' будет блокироваться до тех пор, пока эти два потока не вызовут его. Если один из них не назовет его, другой будет заблокирован навсегда. –

+0

спасибо за объяснение. –

Смежные вопросы