Логотип коледжу
Оптико-механічний фаховий коледж

Лабораторна робота №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 з назвою роботи та командами компіляції.

Питання для захисту

  1. Які аргументи отримує pthread_create і для чого потрібен четвертий параметр?
  2. Що робить pthread_join?
  3. Чому для компіляції потокових програм потрібен прапор -pthread?
  4. Чому порядок завершення потоків не гарантований?
  5. У чому різниця між потоком і процесом у Linux?

Пов'язані лекції

Ресурси

Здати роботу