2013-12-15 2 views
0

Я написал следующую программу. Я ожидаю, что эта функция напечатает 1, 2 последовательно, но программа ждет некоторое время (например, 10 секунд), а затем распечатает весь результат.параллельное выполнение потоков ненадежное поведение

вот код:

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

void *thread_function(void *arg); 
char message[] = "Hello World"; 

int run_now = 1; 

int main() 
{ 
    int res; 

    pthread_t a_thread; 
    void *thread_result; 

    res = pthread_create(&a_thread, NULL, thread_function, (void *)message); 
    if (res != 0) 
    { 
     perror("Thread creation failed"); 
     exit(EXIT_FAILURE); 
    } 

    int print_count1 = 0; 
    while(print_count1++ < 20) 
    { 
     if (run_now == 1) 
     { 
      printf("1"); 
      run_now = 2; 
     } 
     else 
     { 
      sleep(1); 
     } 
    } 

    printf("\nWaiting for thread to finish...\n"); 

    res = pthread_join(a_thread, &thread_result); 
    if (res != 0) 
    { 
     perror("Thread join failed"); 
     exit(EXIT_FAILURE); 
    } 
    printf("Thread joined. \n");  
    exit(EXIT_SUCCESS); 
} 

void *thread_function(void *arg) 
{ 
    int print_count2 = 0; 
    while(print_count2++ < 20) 
    { 
     if (run_now == 2) 
     { 
      printf("2"); 
      run_now = 1; 
     } 
     else 
     { 
      sleep(1); 
     } 
    } 
} 

я рассчитывать на 1, 2 печатается каждые 1 секунды, но программа ожидает несколько раз, а затем печатает все строки целиком. может кто-нибудь сказать мне, в чем причина?

+1

попробовать его с 'Е ("2 \ п");'. – duedl0r

+4

'run_now' должен быть защищен или сделан атомарным, ваш код, поскольку он имеет расы данных = неопределенное поведение. – Mat

+0

спасибо, что работает! – muradin

ответ

1

Ну, есть несколько проблем с вашей программой. Во-первых, printf может хранить содержимое внутри, поэтому вы их не увидите. Вероятно, это то, о чем вы говорите. Вам необходимо fflushstdout после каждого printf. Более серьезная проблема заключается в том, что ваша программа неверна, поскольку она использует неатомную, энергонезависимую переменную для связи.

В вашем thread_function компилятор может свободно перемещать нагрузку run_now из цикла и работать только с копией регистра, поэтому цикл никогда не заметит, что другой поток изменил значение в памяти. Вы должны всегда использовать атомарные свойства для доступа к таким общим переменным.

+0

Ваша первая часть ответа полностью верна, но, как вы упомянули во второй части, если второй поток работает с копией 'run_now', тогда нить никогда не будет завершена, а из-за сна она всегда находится в состоянии ожидания. поэтому компилятор не справляется с этой ситуацией. Я прав? – muradin

+0

Вы не можете полагаться на то, что компилятор не делает этого, он вправе в любое время изменить свой разум, использовать атомику (и я не имею в виду ОМУ :-), или если вам нужно , мьютексы/критические разделы – mornfall

1

Используйте fprintf(stderr,"...") вместо printf или добавьте fflush(stdout) после каждого printf.

Это связано с тем, что stout сбрасывается только тогда, когда ОС решает очистить его, в то время как stderr сбрасывается, как только вызывается fprintf.

+0

Насколько я знаю, stdout и stderr ничем не отличаются друг от друга, насколько это касается (POSIX, в любом случае) операционной системы. Это всего лишь файловые дескрипторы. Кроме того, если вы печатаете на 'stderr', но flush' stdout', это не принесет много пользы. :-) – mornfall

+0

@mornfall Я сказал fprintf (stderr, "...") ИЛИ printf + fflush (stdout). Я не знаю, отличаются ли они, но я знаю, что вывод printf отображается, когда ОС решает распечатать, а fprintf (stderr ..., печатает в реальном времени. – HAL9000

+0

На самом деле, fflush и stderr autoflush - это не ОС, это просто libc. – mornfall

3

int run_now обращается одновременно, и поэтому его доступ нуждается в для защиты.

Для этого использовать семафор, например:

... 

int run_now = 1; 
pthtread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

int main() 
{ 
    ... 

    while(print_count1++ < 20) 
    { 
     pthread_mutex_lock(&mutex); 
     int run_now_equals_1 = (run_now == 1); 
     if (run_now_equals_1) 
     { 
      printf("1"); 
      run_now = 2; 
     } 
     pthread_mutex_unlock(&mutex); 

     if (!run_now_equals_1) 
     { 
      sleep(1); 
     } 
    } 

    ... 
} 

void *thread_function(void *arg) 
{ 
    int print_count2 = 0; 

    while(print_count2++ < 20) 
    { 
     pthread_mutex_lock(&mutex); 
     int run_now_equals_2 = (run_now == 2); 
     if (run_now_equals_2) 
     { 
      printf("2"); 
      run_now = 1; 
     } 
     pthread_mutex_unlock(&mutex); 

     if (!run_now_equals_2) 
     { 
      sleep(1); 
     } 
    } 
} 
+0

Ну, мьютексы pthread чрезвычайно дороги по сравнению с атомным сравнением и свопом (хотя и более портативным). – mornfall

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