Нить в програмуванні є важливим механізмом в наш час. Тому я вирішив присвятити кілька статей цієї теми.
У Linux же трохи по-іншому. Сутність процесу така ж, як і в Windows - це програма, що виконується зі своїми даними. Але ось потік в Linux є окремим процесом (можна зустріти назву як «легкий процес», LWP). Різниця така ж - процес окрема програма зі своєю пам'яттю, не може безпосередньо звернутися до пам'яті іншого процесу, а ось потік, хоч і окремий процес, має доступ до пам'яті процесу-батька [2]. LWP процеси створюються за допомогою системного виклику clone () із зазначенням певних прапорів.
Але також є така річ, яка називається «POSIX Threads» - бібліотечка стандарту POSIX, яка організовує потоки (вони ж нитки) всередині процесу. Тобто тут вже розпаралелювання відбувається в рамках одного процесу.
І тут постає питання відмінності термінів «потік», «процес», «нитка» і т.д. Проблема в тому, що в англомовній літературі ці терміни визначаються однозначно, у нас же з нашим великим і могутнім є протиріччя, що може привести до дикого дисонансу.
Але це все в загальних рисах, для більш точної інформації слід звернутися до відповідної літератури, або до офіційної документації, можна почитати man'и. В кінці статті я приведу декілька корисних посилань на ресурси, де більш детально розписано як все працює, а поки займемося практикою.
Я розгляну два варіанти «розпаралелювання» програми - створення потоку / нитки за допомогою функцій з pthread.h (POSIX Threads), або створення окремого процесу за допомогою функції fork ().
У цій статті розглянемо потоки з бібліотеки pthread.
Шаблон коду для роботи з потоками виглядає наступним чином:
Як видно з коду, сутність потоку втілена в функції, в даному випадку, threadFunc. Ім'я такої функції може бути довільним, а ось повертається тип і тип вхідного аргументу повинні бути строго void *. Ця функція буде виконуватися в окремому потоці виконання, тому необхідно з особливою обережністю підходити до реалізації даної функції через доступ до однієї і тієї ж пам'яті батьківського процесу багатьма потоками. Завершення досягається декількома варіантами: потік досяг точки завершення (return, pthread_exit (0)), або потік був завершений ззовні.
Створення потоку відбувається за допомогою функції pthread_create (pthread_t * tid, const pthread_attr_t * attr, void * (* function) (void *), void * arg), де: tid - ідентифікатор потоку, attr - параметри потоку (NULL - атрибути за замовчуванням , подробиці в man), function - покажчик на потокову функцію, в нашому випадку threadFunc і arg - покажчик на дані, що передаються в потік.
Функція pthread_join очікує завершення потоку thread. Другий параметр цієї функції - результат, що повертається потоком.
Спробуємо за цим шаблоном написати програму, яка виконує щось корисне. Наприклад, спробуємо скласти дві матриці і зберегти результат у третій результуючої матриці. Для вирішення даного завдання вже необхідно подумати про правильний розподіл даних між потоками. Я реалізував простий алгоритм - скільки рядків в матриці, стільки потоків. Кожен потік складає елементи рядка першої матриці з елементами рядка другуматриці і зберігає результат в рядок третин матриці. Виходить, що кожен потік працює рівно зі своїми даними і таким чином виключається доступ одного потоку до даних іншого потоку. Приклад програми з використанням потоків pthread вигляди наступним чином:
Результат виконання в ОС CentOS 7 з висновком в консоль вигляди наступним чином:
Для компіляції програми з використанням pthread, лінковщік слід вказати прапор -lpthread (або підключити бібліотеку pthread в IDE). Команда для компіляції в gcc буде виглядати приблизно так:
gcc -std = c99 main.c -o main.out -lpthread
Системи збирання - make
Витік пам'яті pthread?
One thought on "Програмування C в Linux - потоки pthreads"
Привіт Гість
Мене звуть Владислав, він же Tetraquark. Я злегка цікавлюся програмуванням і люблю всім розповідати, чим я цікавлюся. Саме цим я тут і займаюся.
Сподіваюся вам сподобається!