Из вашего описания в комментариях, похоже, что вы пытаетесь синхронизировать 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)
.
Вам нужно будет выполнить свою собственную реализацию, но 'sleep (3)' обычно не то, что вы хотите в потоках, потому что оно может использовать сигналы ('SIGALRM'), и не гарантируется, что' SIGALRM' является доставляются на тот же конкретный поток (если другие потоки ждут какого-то сигнала, это может быть проблематично, и ваш поток может спать навсегда). Вам придется пойти на что-то более умное, например. 'select (2)' с FIFO или трубой. Вы можете уточнить свой вопрос? Включите распространенные случаи использования, почему вы хотите это сделать и т. Д. Это важно, чтобы мы могли выбрать правильный дизайн. –
У меня есть два основных потока: один для отправки данных по сети, другой - данные из сети. Помимо джиттера, принимающий поток выполняет проверку и проверку и некоторое управление файлами, которое вовремя может привести к тому, что он перетащит за собой. Мне нравится делать что-то вроде микросота для отправителя, чтобы получатель мог догнать. sched_yield() не поможет в этом случае, потому что HW имеет многоядерный процессор с более чем 40 ядрами. –
Я вижу. Это важно, вы должны отредактировать вопрос, чтобы включить эти данные. Я добавил ответ, который, я считаю, является тем, что вы ищете. –