Лабораторна робота №4. Створення та керування потоками POSIX Threads
Мета
Навчитися створювати потоки через pthread_create, передавати потокам аргументи, очікувати їх завершення через pthread_join і збирати результати роботи потоків.
Підготовка
Перед виконанням опрацюйте пов'язану лекцію:
Для роботи потрібне Linux-середовище з GCC. Потоки POSIX у Linux вимагають прапора -pthread під час компіляції. Робота виконується в межах папки lab-04.
Завдання
Завдання 1. Відтворіть програму з потоками
У папці lab-04 створіть файл threads_demo.c:
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 4
typedef struct {
long index;
long result;
} Job;
void *worker(void *arg) {
Job *job = (Job *)arg;
job->result = 0;
for (long i = 1; i <= 10; i++) {
job->result += i + job->index * 1000;
}
printf("Потік %ld: часткова сума = %ld\n", job->index, job->result);
return NULL;
}
int main(void) {
pthread_t threads[NUM_THREADS];
Job jobs[NUM_THREADS];
for (long i = 0; i < NUM_THREADS; i++) {
jobs[i].index = i;
jobs[i].result = 0;
if (pthread_create(&threads[i], NULL, worker, &jobs[i]) != 0) {
perror("pthread_create");
return 1;
}
}
long total = 0;
for (long i = 0; i < NUM_THREADS; i++) {
if (pthread_join(threads[i], NULL) != 0) {
perror("pthread_join");
return 1;
}
total += jobs[i].result;
}
printf("Загальна сума: %ld\n", total);
return 0;
}
Скомпілюйте з прапором -pthread та запустіть два-три рази:
gcc -Wall -Wextra -pedantic -pthread -o threads_demo threads_demo.c
./threads_demo
./threads_demo
./threads_demo
Поясніть: що отримує функція потоку через аргумент, чому кожен потік має власний елемент jobs[i], і чому порядок рядків «Потік N: ...» може змінюватися між запусками.
Завдання 2. Змініть кількість потоків і обсяг роботи
Змініть NUM_THREADS з 4 на 8, а верхню межу циклу в worker — з 10 на 5. Перед запуском обчисліть очікувану часткову суму для потоку i та загальну суму. Запишіть прогноз, потім скомпілюйте й порівняйте.
Формула для одного потоку: (1 + ... + 5) + i * 1000 * 5. Загальна сума — сума часткових сум усіх потоків.
Завдання 3. Прикладний: паралельне сумування масиву
Створіть файл sum_array.c, у якому потоки сумують непересічні зрізи спільного масиву:
#include <pthread.h>
#include <stdio.h>
#define ARR_SIZE 100
#define NUM_THREADS 4
typedef struct {
int start;
int end;
long sum;
} Slice;
static int data[ARR_SIZE];
void *sum_slice(void *arg) {
Slice *s = (Slice *)arg;
s->sum = 0;
for (int i = s->start; i < s->end; i++) {
s->sum += data[i];
}
return NULL;
}
int main(void) {
for (int i = 0; i < ARR_SIZE; i++) {
data[i] = i + 1;
}
pthread_t threads[NUM_THREADS];
Slice slices[NUM_THREADS];
int per = ARR_SIZE / NUM_THREADS;
for (int t = 0; t < NUM_THREADS; t++) {
slices[t].start = t * per;
slices[t].end = (t + 1) * per;
slices[t].sum = 0;
if (pthread_create(&threads[t], NULL, sum_slice, &slices[t]) != 0) {
perror("pthread_create");
return 1;
}
}
long total = 0;
for (int t = 0; t < NUM_THREADS; t++) {
if (pthread_join(threads[t], NULL) != 0) {
perror("pthread_join");
return 1;
}
total += slices[t].sum;
}
printf("Сума 1..%d = %ld\n", ARR_SIZE, total);
return 0;
}
Скомпілюйте та запустіть:
gcc -Wall -Wextra -pedantic -pthread -o sum_array sum_array.c
./sum_array
Поясніть, чому кожен потік змінює лише власну структуру slices[t], і чому в цьому завданні не виникає стану гонитви, хоча всі потоки читають спільний масив data.
Очікуваний результат
Для завдання 1 часткові суми для потоку i обчислюються за формулою 55 + i * 1000 * 10, тобто:
Потік 0: часткова сума = 55
Потік 1: часткова сума = 10055
Потік 2: часткова сума = 20055
Потік 3: часткова сума = 30055
Загальна сума: 60220
Порядок чотирьох перших рядків може відрізнятися між запусками. Для завдання 3 програма має вивести:
Сума 1..100 = 5050
Структура репозиторію
operational-systems/
├── README.md
├── lab-01/
├── lab-02/
├── lab-03/
└── lab-04/
├── threads_demo.c
└── sum_array.c
Що здати
- Посилання на ваш особистий репозиторій
operational-systems, надіслане у формі здачі на поточній сторінці. - Папку
lab-04з файламиthreads_demo.cіsum_array.c. - Оновлений кореневий
README.mdз назвою роботи та командами компіляції.
Питання для захисту
- Які аргументи отримує
pthread_createі для чого потрібен четвертий параметр? - Що робить
pthread_join? - Чому для компіляції потокових програм потрібен прапор
-pthread? - Чому порядок завершення потоків не гарантований?
- У чому різниця між потоком і процесом у Linux?
