2016-01-29 2 views
3

Я столкнулся с проблемой многопоточности. Пожалуйста, обратите внимание на следующий код:Каков поток выполнения в многопоточном режиме, C?

#include<stdio.h> 
#include<pthread.h> 

void* functionA(void*); 
void* functionB(void*); 


int main() 
{ 
    pthread_t tid[2]; 
    pthread_attr_t arg; 

    for(int i = 0; i<2; ++i) 
    { 
     pthread_attr_init(&arg); 

     if(i == 0) 
     { 
      int x = 0; 
      pthread_create(&tid[i], &arg, functionA, (void*)&x); 
     } 

     else if(i == 1) 
     { 
      int x = 6; 
      pthread_create(&tid[i], &arg, functionB, (void*)&x); 
     } 
    } 

// wait for both threads to finish execution...  
pthread_join(tid[0], NULL); 
pthread_join(tid[1], NULL); 

return 0; 
} 


//.........................DEFINATIONS........................ 

void* functionA(void* x) 
{ 
    int Index = *((int*)x); 
    printf("First: %d\n",Index); //..................... LINE M 
} 


void* functionB(void* x) 
{ 
    int Index = *((int*)x); 
    printf("Second: %d\n",Index); //....................... LINE N 
} 

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

First: 0       (from Line M) 
    Second: 6      (from Line N) 

но фактический выход,

Second: 6 
    First: 6 

Теперь я действительно удивлен, увидев это, и я не знаю, что происходит. Не только secondFunction начинает свое выполнение сначала, но и обе функции показывают одинаковое значение, то есть 6, и это не имеет смысла для меня. Может ли кто-нибудь объяснить Объясните мне, что здесь происходит ???? Заранее спасибо ...

+6

'x' выходит из сферы действия, как только вы создаете свою нить, поэтому ваш код демонстрирует UB. –

+0

Попробуйте сделать это в ваших потоках: 'printf ("% p \ n ", x);', я думаю, вы увидите, что указатель для обоих потоков одинаковый, потому что ваш 'int x = 0/6; 'находятся в локальной области, поэтому они могут быть выделены по тому же адресу. –

+2

Итак, вы используете потоки, но вы ожидаете определенного порядка исполнения между ними? Как вы думаете, какая разница между запуском потока и вызовом метода? – davmac

ответ

3

две вещи

1) Здесь х переменный объем ограничиваются лишь, что если блок. так что в вашей функции потока, когда вы обращаетесь к этому указателю, его область исчезла. Таким образом, вы получаете доступ к незаконной памяти в функции потока, которая является неправильной, и она создаст неопределенное поведение.

В ответ на Ваш один комментарий

так есть ли способ, что вместо переменной, я могу отправить непосредственно постоянное число, например pthread_create (& TID [я], & Arg, functionA, 6)? ?

POSIX threads - это API C. C не предоставляет языковых средств, таких как экземпляров копий, и поэтому невозможно скопировать любой объект по значению .

Вам нужно всегда передавать что-либо только указателями.

2) Приоритет выполнения потоков - это тотальная ОС и расписание, и вы не можете предполагать последовательность этих потоков.

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

+2

downvoter, пожалуйста, комментарий ... Я люблю учиться на своих ошибках. –

2

Вы передаете указатели на недолговечные локальные переменные в потоки, вызывая неопределенное поведение, как только эти области выходят. Вероятно, оба потока видят тот же адрес: x.

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

pthread_t tid[2]; 
pthread_attr_t arg; 
int x[2] = { 0, 6 }; 
void * (*func[])(void *) = { functionA, functionB }; 

for(int i = 0; i < 2; ++i) 
{ 
    pthread_attr_init(&arg); 
    pthread_create(&tid[i], &arg, func[i], &x[i]); 
} 

// wait for both threads to finish execution...  
pthread_join(tid[0], NULL); 
pthread_join(tid[1], NULL); 

Это работает, так как x массив будет жить мимо звонков pthread_join(). Также нет необходимости бросать указатель, int * автоматически преобразуется в void * в C.

Также ваше предположение о порядке начала потоков является ложным, таких гарантий нет.

+0

, так есть ли какой-либо путь вместо переменной, я могу напрямую отправлять постоянное число, например pthread_create (& tid [i], & arg, functionA, 6) ?? –

+1

@MuzahirHussain: вы можете 'malloc' буфер для переменной прямо перед запуском потока, передать указатель на этот буфер в поток и позволить потоку сам« освободить »буфер, как только это будет сделано с его помощью. – datenwolf

+1

Объявление указателя массива указателей функций неверно. Функции возвращают 'void *' not 'void', поэтому объявление должно быть похоже на:' void * (* func []) (void *) = {functionA, functionB}; ' – user3629249

2

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

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

Длинные истории Короче: нет никакой гарантии.

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

0

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

Это предположение неверно, что зависит от планирования потоков, которые управляют ОС ,

Если вы хотите увидеть правильный выход объявить две переменные в main() функции сферы (до for цикла)

int x = 0; 
int y = 6; 

, а также вносить изменения в

pthread_create(&tid[i], &arg, functionA, (void*)&x); 
pthread_create(&tid[i], &arg, functionB, (void*)&y); 

Делая это, вы получите по крайней мере, правильный вывод ,

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