2016-04-11 2 views
2

В многопоточном сценарии я пытаюсь реализовать таймер POSIX, в котором по истечении таймера должен быть пробужден следующий поток того же процесса (одновременно выполняется только один поток, а все остальные находятся в блочном состоянии). В таймере я использую sigev_notify = SIGEV_THREAD_ID, так как ни я не хочу, чтобы какой-либо обработчик обслуживал сигнал, и я не хочу создавать новый поток после истечения срока действия таймера.Таймер POSIX с sigev_notify = метод SIGEV_THREAD_ID

//Import 
#define _GNU_SOURCE 
#define _POSIX_C_SOURCE 199309 
#include <sched.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <unistd.h> 
#include <signal.h> 
#include <errno.h> 
#include <semaphore.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <syscall.h> 
#define NUM_THREADS 10 

#define CLOCKID CLOCK_REALTIME 

int ret; 
//pthread_cond_t  condA[NUM_THREADS+1] = PTHREAD_COND_INITIALIZER; 
pthread_cond_t  condA = PTHREAD_COND_INITIALIZER; 
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER; 

sem_t sem[NUM_THREADS]; 
sem_t mute; 

timer_t timer1; 
pthread_t tid[NUM_THREADS]; 
int state = 0; 
int thread_count = 1; 
int arr_tid[NUM_THREADS]; 
struct itimerspec new_value, old_value; 

struct sigaction action; 
struct sigevent sevent; 
sigset_t set; 
int signum = SIGALRM; 

void *threadA(void *data_) 
{ 
    cpu_set_t my_set;   
    CPU_ZERO(&my_set);  
    CPU_SET(2, &my_set);  
    sched_setaffinity(0, sizeof(cpu_set_t), &my_set); 

    //struct itimerspec new_value, old_value; 

    int i = 0, value; 
    int sid; 
    FILE *fp; 
    fp=fopen("ipc.out","a");  

    long int loopNum; 
    int turn = (intptr_t)data_; 
    struct timespec tval_result, tval_result2; 

    if(thread_count < NUM_THREADS) 
    { 
     thread_count++; 
     sid = syscall(SYS_gettid); 
     arr_tid[turn] = sid; 
     fprintf(fp,"thread_%d %d\n", turn, sid); 
     //printf("Blocked %d ->%d\n", turn, thread_count); 
     pthread_mutex_lock(&mutex); 
     pthread_cond_wait(&condA, &mutex); 
     pthread_mutex_unlock(&mutex); 
    } 
    else 
    { 
     arr_tid[turn] = syscall(SYS_gettid); 
    } 

    for (value = 0; value < NUM_THREADS; ++value) 
    { 
     printf("%d\n",arr_tid[value]); 
    } 
    //printf("rpg\n"); 
    pthread_mutex_lock(&mutex); 
    pthread_cond_broadcast(&condA); 
    pthread_mutex_unlock(&mutex); 
    //printf("unblocked\n"); 
    fclose(fp); 

    if (turn > 0) 
     { 
      if (sigwait (&set, &signum) == -1) 
       perror ("sigwait"); 
      //sleep(1); 
      //printf("thread %d is sleeping\n", turn); 
     } 
    while(1) 
    { 
     ret = sem_wait(&sem[turn]); 
     if (ret) 
     { 
      printf("Error in Sem Post\n"); 
     } 
     //printf("this isn't the end of the world!!!\n"); 
     sevent.sigev_notify = SIGEV_THREAD_ID; 
     sevent._sigev_un._tid = arr_tid[(turn+1)%10]; 
     sevent.sigev_signo = signum; 

     sigemptyset(&set); 
     sigaddset(&set, signum); 
     sigprocmask(SIG_BLOCK, &set, NULL); 

     printf("Thread # -> %d\n", turn); 
     clock_gettime(CLOCKID, &tval_result); 
     do 
     { 
      clock_gettime(CLOCKID, &tval_result2); 
     } while((tval_result2.tv_sec - tval_result.tv_sec)*1000000000+(tval_result2.tv_nsec - tval_result.tv_nsec)<=12000);  
       //printf("Timestamp : %ld %ld\n", tval_result2.tv_sec, tval_result2.tv_nsec); 
     // printf("Before creating timer\n"); 

     new_value.it_interval.tv_sec = 0; 
     new_value.it_interval.tv_nsec = 0; 
     new_value.it_value.tv_sec = 0; 
     new_value.it_value.tv_nsec = 15000; 
     printf("next thread to be signalled %d\n", arr_tid[turn+1]); 
     if (timer_settime (timer1, 0, &new_value, NULL) == -1) 
      perror ("timer_settime"); 
     printf("yy\n"); 
     ret = sem_post(&sem[(state+1)%NUM_THREADS]); 
     if (ret) 
     { 
      printf("Error in Sem Post\n"); 
     } 
     state++; 
     //printf("yy\n"); 
     //sleep(1); 
     if (sigwait (&set, &signum) == -1) 
      perror ("sigwait"); 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    int data = 0; 
    int err, i; 
    int sid = syscall(SYS_gettid); 
    //struct itimerspec new_value, old_value; 

    FILE *fp; 
    fp=fopen("ipc.out","a");  
    fprintf(fp,"Mainthread %d\n",sid); 
    fclose(fp); 
    if (timer_create (CLOCK_REALTIME, &sevent, &timer1) == -1) 
     perror ("timer_create"); 

    sem_init(&sem[0], 0, 1); 
    //sem_init(&sem[1], 0, 0); 
    //sem_init(&sem[2], 0, 0); 
    for (i = 1; i < NUM_THREADS; ++i) 
     { 
      sem_init(&sem[i], 0, 0); 
     } 
    while(data < NUM_THREADS) 
    { 
     //create our threads 
     err = pthread_create(&tid[data], NULL, threadA, (void *)(intptr_t)data); 
     if(err != 0) 
      printf("\ncan't create thread :[%s]", strerror(err)); 
     data++; 
    } 
    pthread_exit(NULL); 
} 

Компиляция:$ НКУ filename.c -lrt -lpthread

Я использовал семафоров для синхронизации нитей так, что потоки выполняются в определенной последовательности.

Я не получаю желаемый результат. Это должно быть как через 15 микросекунд таймер должен истечь, а следующая нить должна быть разбужена, но этого не происходит. Я использовал sigwait(), чтобы заблокировать потоки. Есть ли другие альтернативы для блокировки потока и пробуждения с помощью таймера? Каковы сигналы, которые могут быть назначены в signum? Могу ли я использовать pthread_cond_signal() вместо SIGALRM?

+0

Кажется немного бессмысленным .... –

+0

@MartinJames В соответствии с рабочей страницей sigwait() любое количество потоков, ожидающих сигнал, можно подобрать для обслуживания сигнала.Что нежелательно в моем случае. Таким образом, если есть какой-то механизм специально для пробуждения потока по истечении таймера, то этого будет достаточно для моей проблемы. (например, в pthread_cond_signal(), где мы можем отправить сигнал в конкретный поток, ожидающий переменную cond). – Scissor

ответ

1

Хм .. ОК.

Инициализируйте свой семафор на один блок и подождите, пока все потоки ожидают его.

Если поток получает блок, он сохраняет текущее время «startTime» в каком-то статическом/глобальном/безотносительно (безопасно, поскольку только один поток может иметь семабуту за раз) и уходит, чтобы сделать свое дело/с. Когда это будет сделано, он получит текущее время «endTime», оговорившись, сколько времени займет «endTime-startTime», и вычитает это из требуемого интервала времени между потоками - теперь есть, как долго осталось до следующего потока, remainingInterval. Если оставшееся значениеInterval меньше 0, оно равно 0. Оно преобразует оставшееся значениеInterval в любой элемент, необходимый для usleep()/Sleep(), и спит так долго, а затем отправляет свой блок в семафор. Затем может быть запущен другой поток, и поток, который только что сделал свою работу, может зацикливаться и снова ждать семафора, если захочет.

Просто невозможно запустить несколько потоков за раз, независимо от того, как долго/сортировать/какими бы ни были работы.

Нет явного таймера, мьютекса, condvar и т. Д. И т. Д., Требуется только один семафор.

+0

Продуманно написанный ... Желаемый «оставшийся интервал», который вы упомянули, должен быть 1 микросекунда или даже меньше. Итак, практически, я не могу использовать sleep() или usleep(). Даже если я иду с clock_nanosleep(), я не могу достичь этого разрешения в текущих процессорах (исправьте меня, если я ошибаюсь). Итак, для этого мне нужен многопоточный механизм. Я надеялся, что смогу добиться столь тонкой детализации с таймером. – Scissor

+0

1 us? Вы не сможете на какой-либо ОС, о которой я знаю. Я все еще не понимаю, зачем вам все эти темы в любом случае - только один может работать одновременно, так зачем беспокоиться? –

+0

Okey. Я использую несколько потоков, потому что виртуальное время выполнения каждого потока будет меньше, чем процесс, который будет выполняться в этом «остальномInterval». Все потоки выполняют одну и ту же работу один за другим последовательно. Эта межпоточная длительность (которую я хочу около 1 нас) должна быть зарезервирована для процесса на одном ядре. Таким образом, вся эта настройка будет такой, что, как только один поток завершит работу, он перейдет в состояние блока. Между тем, этот другой процесс начнет выполнение для 1us. К этому времени следующий поток должен быть пробужден таймером (в моей реализации). – Scissor

1

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

Это то, что должно быть сделано: Таймер и установка времени, должно происходить на уровне процесса один раз, а не для каждого потока.

Далее создайте все потоки и сохраните идентификаторы потоков в массиве и заставите их ждать в зависимости от некоторого состояния (pthread_cond_wait).

Когда таймер истекает и посылает сигнал, прокручивайте потоки-идентификаторы и просыпайтесь по одному потоку за раз.

+0

Итак, мне нужно создать обработчик сигнала для каждого потока, чтобы обслуживать сигнал, который появляется после истечения срока действия таймера? Кроме того, в этом обработчике сигнала я должен использовать pthread_cond_signal() для пробуждения следующего потока, ожидающего переменную cond (если это безопасно для вызова из обработчика). Но я использую метод SIGEV_THREAD_ID, так что один и тот же запущенный поток пробуждается следующим, поэтому нет необходимости создавать обработчик. Тогда как я должен просыпать поток, ожидающий pthread_cond_wait()? Могу ли я использовать pthread_cond_signal() вместо SIGALRM в моем коде? – Scissor

+0

Думаю, вы этого не поняли. Будет один обработчик сигнала. Время timer_create и timer_settime произойдет только один раз в main(). Как только таймер истечет, и обработчик сигнала поймает его, внутри этого обработчика сигнала вам просто нужно найти следующий поток в последовательности из вашего массива thread-id и разбудить его. – kingsmasher1

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