Основы программирования в Linux - Нейл Мэтью
Шрифт:
Интервал:
Закладка:
}
printf("Waiting for thread to finish...n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
int i, res;
res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (res != 0) {
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}
res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
if (res != 0) {
perror{"Thread pthread_setcanceltype failed");
exit(EXIT_FAILURE);
}
printf("thread_function is runningn");
for(i = 0; i < 10; i++) {
printf("Thread is still running (%d)...n", i);
sleep(1);
}
pthread_exit(0);
}
Когда вы выполните эту программу, то увидите следующий вывод, демонстрирующий отмену потока:
$ ./thread7
thread_function is running
Thread is still running (0)...
Thread is still running (1)...
Thread is still running (2)...
Canceling thread...
Waiting for thread to finish...
$
Как это работает
После того как новый поток был создан обычным способом, основной поток засыпает (чтобы дать новому потоку время для запуска) и затем отправляет запрос на отмену потока.
sleep(3);
printf("Cancelling thread...n");
res = pthread_cancel(a_thread);
if (res != 0) {
perror("Thread cancelation failed");
exit(EXIT_FAILURE);
}
В созданном потоке вы сначала задаете состояние отмены, чтобы разрешить отмену потока:
res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (res != 0) {
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}
Далее вы задаете тип отмены PTHREAD_CANCEL_DEFERRED:
res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
if (res != 0) {
perror("Thread pthread_setcanceltype failed");
exit(EXIT_FAILURE);
}
И в конце поток ждет отмену:
for (i = 0; i < 10; i++) {
printf("Thread is still running (%d)...n", i);
sleep(1);
}
Потоки в изобилии
До настоящего момента у нас всегда был обычный поток исполнения программы, создающий еще только один поток. Тем не менее мы не хотим, чтобы вы думали, что можно создать только один дополнительный поток (упражнение 12.8).
Упражнение 12.8. Много потоковВ заключительном примере этой главы thread8.c мы покажем, как создать несколько потоков в одной и той же программе и затем собрать их снова в последовательности, отличающейся от порядка их создания.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 6
void *thread_function(void *arg);
int main() {
int res;
pthread_t a_thread[NUM_THREADS];
void *thread_result;
int lots_of_threads;
for (lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) {
res = pthread_create(&(a_thread[lots_of_threads]), NULL, thread_function, (void*)&lots_of_threads);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(1);
}
printf("Waiting for threads' to finish...n");
for(lots of_threads = NUM_THREADS - 1; lots_of_threads >= 0; lots_of_threads--) {
res = pthread_join(a_thread[lots_of_threads], &thread_result);
if (res == 0) {
printf("Picked up a threadn");
} else {
perror("pthread_join failed");
}
}
printf("All donen");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
int my_number = *(int*)arg;
int rand_num;
printf("thread_function is running. Argument was %dn", my_number);
rand_num = 1 + (int)(9.0*rand() / (RAND_MAX+1.0));
sleep(rand_num);
printf("Bye from %dn", my_number);
pthread_exit(NULL);
}
Выполнив эту программу, вы получите следующий вывод:
$ ./thread8
thread_function is running. Argument was 0
thread_function is running. Argument was 1
thread_function is running. Argument was 2
thread_function is running. Argument was 3
thread_function is running. Argument was 4
Bye from 1
thread_function is running. Argument was 5
Waiting for threads to finish...
Bye from 5
Picked up a thread
Bye from 0
Bye from 2
Bye from 3
Bye from 4
Picked up a thread
Picked up a thread
Picked up a thread
Picked up a thread
Picked up a thread
All done
Как видите, вы создали много потоков и разрешили им завершаться в произвольной последовательности. В этой программе есть маленькая ошибка, которая проявит себя, если вы удалите вызов sleep из цикла, запускающего потоки. Мы включили ее, чтобы показать, как вы должны быть внимательны при написании программ, применяющих потоки. Вы нашли ее? В следующем разд. "Как это работает" будет дано объяснение.
Как это работает
На сей раз вы создаете массив идентификаторов потоков:
pthread_t a_thread[NUM_THREADS];
и заключаете в цикл создание нескольких потоков:
for (lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) {
res = pthread_create(&(a_thread[lots_of_threads]), NULL,
thread_function, (void *)&lots_of_threads);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(1);
}
Затем потоки сами по себе ждут в течение случайного промежутка времени, прежде чем начать выполнение:
void *thread_function(void *arg) {
int my_number = *(int *)arg;
int rand_num;
printf("thread_function is running. Argument was %dn", my_number);
rand_num = 1+(int)(9.0* rand()/(RAND_MAX+1.0));
sleep(randnum);
printf("Bye from %dn", my_number);
pthread_exit(NULL);
}
В это время в основном (исходном) потоке вы ждете, чтобы собрать потоки, но не в том порядке, в каком вы их создали:
for (lots_of_threads = NUM_THREADS - 1; lots_of_threads >= 0; lots_of_threads--) {
res = pthread_join(a_thread[lots_of__threads], &thread_result);
if (res == 0) {
printf("Picked up a threadn");
} else {
perror("pthread_join failed");
}
}
Если вы попробуете выполнить программу без вызова sleep, то увидите странный эффект: некоторые потоки запускаются с одним и тем же номером, например, вы можете получить вывод, похожий на следующий:
thread_function is running. Argument was 0
thread_function is running. Argument was 2
thread_function is running. Argument was 2
thread_function is running. Argument was 4
thread_function is running. Argument was 4
thread_function is running. Argument was 5
Waiting for threads to finish...
Bye from 5
Picked up a thread
Bye from 2
Bye from 0
Bye from 2
Bye from 4
Bye from 4
Picked up a thread
Picked up a thread
Picked up a thread
Picked up a thread
Picked up a thread
All done
Вы догадались, что произошло? Потоки запускаются, используя локальную переменную как аргумент функции потока. Эта переменная обновляется в цикле. Далее приведены ошибочные строки:
for (lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) {
res = pthread_create(&(a_thread[lots_of_threads]), NULL,
thread_function, (void *)&lots_of_threads);
Если поток main выполняется достаточно быстро, он может искажать аргумент (lots_of_threads) для некоторых потоков. Поведение, подобное этому, наблюдается, когда недостаточно внимания уделяется совместно используемым переменным и множественным путям исполнения (multiple execution paths). Мы предупреждали вас о том, что программирование потоков требует повышенного внимания при разработке! Для исправления ошибки вам следует передавать непосредственно значение следующим образом:
res = pthread_create(&(a_thread[lots_of_threads]), NULL,
thread_function, (void *)lots_of_threads);
и конечно изменить thread_function:
void *thread_function(void *arg) {
int my_number = (int)arg;
Все исправления, выделенные цветом, показаны в программе thread8a.c.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define NUM_THREADS 6
void *thread_function(void *arg);
int main() {
int res;
pthread_t a_thread[NUM_THREADS];
void *thread_result;
int lots_of_threads;
for (lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++) {
res = pthread_create(&(a_thread[lots_of_thread]), NULL,
thread_function, (void*)lots_оf_threads);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
}
printf("Waiting for threads to finish...n");
for (lots_of_threads = NUM_THREADS - 1; lots_of_threads >= 0;
lots of threads--) {
res = pthread_join(a_thread[lots_of_threads], &thread_result);