Створення потоків за допомогою pthread_create, об’єднання потоків за допомогою pthread_join, передача
Створення та очікування потоку
Розглянемо простий приклад
У цьому прикладі всередині основного потоку, у якому працює функція main, створюється новий потік, у якому викликається функція helloWorld. Функція helloWorld виводить на екран привітання. Усередині основного потоку також виводиться вітання. Далі потоки поєднуються.
Новий потік створюється за допомогою функції pthread_create
Функція отримує як аргументи покажчик на потік, змінну типу pthread_t, у якому, у разі успішного завершення зберігає id потоку. pthread_attr_t - атрибути потоку. Якщо використовуються атрибути за замовчуванням, то можна передавати NULL. start_routin – це функція, яка буде виконуватися в новому потоці. arg – це аргументи, які передадуть функції.
Потік може виконувати багато справ і отримувати різні аргументи. Для цього функція, яку буде запущено в новому потоці, приймає аргумент типу void*. За рахунок цього можна обернути всі аргументи, що передаються в структуру. Повертати значення можна також через аргумент, що передається.
У разі успішного виконання функція повертає 0. Якщо відбулися помилки, то можуть бути повернені наступні значення
- EAGAIN– система не має ресурсів для створення нового потоку, або система не може більше створювати потоків, оскільки кількість потоків перевищила значення PTHREAD_THREADS_MAX (наприклад, на одній з машин, які використовуються для тестування, це магічне число дорівнює 2019 )
- EINVAL– неправильні атрибути потоку (передані аргументом attr)
- EPERM– Потік виклику не має належних прав для того, щоб задати потрібні параметри або політики планувальника.
Пройдемо за програмою
Тут ми задаємо набір значень, необхідний обробки можливих помилок.
Це функція, яка працюватиме в окремому потоці. Вона не отримуватиме жодних аргументів. За стандартом вважається, що явний вихід із функції викликає функцію pthread_exit, а значення, що повертається, буде передано при виклику функції pthread_join, як статус.
Тут створюється і одразу ж виконується новий потік. Потік не отримує жодних атрибутів чи аргументів. Після створення потоку відбувається перевірка помилку.
Приводить до того, що основний потік чекатиме на завершення породженого. Функція
Відкладає виконання викликаючого (цю функцію) потоку, доки буде виконано потік thread. Коли pthread_join виконалася успішно, вона повертає 0. Якщо потік явно повернув значення (це те саме значення SUCCESS, з нашої функції), воно буде поміщено в змінну value_ptr. Можливі помилки, які повертає pthread_join
- EINVAL– thread вказує на не поєднується потік
- ESRCH– немає потоку з таким ідентифікатором, який зберігає змінна thread
- EDEADLK– був виявлений дідлок (взаємне блокування), або як об'єднуючий поток вказаний сам викликаючий потік.
Приклад створення потоків із передачею їм аргументів
Нехай ми хочемо передати потоку дані і повернути щось назад. Скажімо, передаватимемо потоку рядок, а повертатимемо з потоку довжину цього рядка.
Так як функція може отримувати тільки покажчик типу void, всі аргументи слід упаковати в структуру. Визначимо новий тип структури:
Тут id – це ідентифікатор потоку (він взагалі не потрібний у нашому прикладі), друге поле це рядок, атретя довжина рядка, який ми будемо повертати.
Усередині функції наводимо аргумент до потрібного типу, виводимо на друк рядок і засовуємо в структуру обчислену довжину рядка.
У тому випадку, якщо все пройшло вдало, то як статус повертаємо значення SUCCESS, а якщо була допущена помилка (у нашому випадку, якщо передано нульовий рядок), то виходимо зі статусом BAD_MESSAGE.
У цьому прикладі створимо 4 потоки. Для 4-х потоків знадобляться масив типу pthread_t довжиною 4, масив переданих аргументів і 4 рядки, які ми і передаватимемо.
Насамперед заповнюємо значення аргументів.
Далі створюємо у циклі нові потоки
Потім чекаємо завершення
Насамкінець ще виводимо аргументи, які тепер зберігають повернені значення. Зауважте, що один із аргументів «поганий» (рядок дорівнює NULL). Ось повний код
Виконайте його кілька разів. Зауважте, що порядок виконання потоків не є детермінованим. Запускаючи програму, можна щоразу отримати інший порядок виконання.