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
#include
#include
#include
#include
#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);
if (res == 0) {
printf("Picked up a thread\n");
} else {
perror("pthread_join failed");
}
}
printf("All done\n");
exit(EXIT_SUCCESS);
}
void* thread_function(void* arg) {
int my_number = (int)arg;
int rand_num;
printf("thread_function is running. Argument was %d\n", my_number);
rand_num = 1+(int)(9.0*rand()/(RAND_MAX+1.0));
sleep(rand_num);
printf("Bye from %d\n", my_number);
pthread_exit(NULL);
}
В этой главе вы узнали, как создать несколько потоков исполнения внутри процесса, которые совместно используют глобальные переменные. Вы рассмотрели два способа управления — семафоры и мьютексы, применяемые потоками для доступа к важным фрагментам кода и данным. Далее вы увидели, как управлять атрибутами потоков и, в особенности, как можно отсоединить потоки от основного, не заставляя его ждать завершения созданных им потоков. После краткого обзора способов формирования в одном потоке запросов на отмену других потоков и вариантов управления такими запросами в потоке, получившем их, мы представили программу с множественными одновременно выполняющимися потоками.
Объем книги не позволяет обсудить все до единой функции и тонкости, связанные с потоками, но теперь у вас достаточно знаний для того, чтобы начать писать собственные программы, применяющие потоки, и изучать глубоко скрытые свойства потоков, читая страницы интерактивного справочного руководства.
Глава 13
Связь между процессами: каналы
В главе 11 вы видели очень простой способ пересылки сообщений между процессами с помощью сигналов. Вы формировали уведомляющие события, которые могли бы применяться для вызова ответа, но передаваемая информация была ограничена номером сигнала.
В этой главе вы познакомитесь с каналами, которые позволяют процессам обмениваться более полезной информацией. В конце этой главы вы примените свои вновь приобретенные знания для новой реализации программы, управляющей базой данных компакт-дисков, в виде клиент-серверного приложения.
В данной главе мы обсудим следующие темы:
□ определение канала;
□ каналы процессов;
□ вызовы каналов;
□ родительские и дочерние процессы;
□ именованные каналы — FIFO;
□ замечания, касающиеся клиент-серверных приложений.
Мы применяем термин "канал" для обозначения соединения потока данных одного процесса с другим. Обычно вы присоединяете или связываете каналом вывод одного процесса с вводом другого.
Большинство пользователей Linux уже знакомы с идеей конвейера, связывающего вместе команды оболочки так, что вывод одного процесса поставляет данные прямо во ввод другого. В случае команд оболочки это делается с помощью символа конвейера или канала, соединяющего команды следующим образом:
cmd1 | cmd2
Командная оболочка организует стандартный ввод и вывод двух команд так, что:
□ стандартный ввод cmd1
поступает с клавиатуры терминала;
□ стандартный вывод cmd1
поставляется cmd2
как ее стандартный ввод;
Читать дальше