Многопоточность в языке Си

Использование множества потоков может значительно увеличить скорость выполнения программы. В настоящее время возможности повышения частоты работы процессора практически исчерпаны и производители идут по пути увеличения ядер процессора. Не все задачи допускают распараллеливание, но там где это возможно прирост может составлять чуть меньшее число раз чем количество ядер (включая виртуальные от технологии Hyper-threading).

Одним из способов использования многопоточности в языке Си является использование заголовочного файла pthread.h и функций pthread_create() и pthread_join():

1) Следует объявить и определить функцию передаваемую потоку типа void * thread_func(void *args). Возвращаемый результат и аргументы должны быть указателями на void. Один из вариантов - создание структуры указатель на которую передастся в эту функцию. В приведенной ниже программе это struct thread_args.

2) Создаются идентификаторы потоков pthread_t - по их количеству pthread_t thread[THREAD_SIZE];

3) Запускаются потоки функцией pthread_create(&thread[i], NULL, thread_func, &arguments[i]). В нее передаются указатель идентификатора потока &thread[i], указатель на функцию потока thread_func, и указатель на аргумент функции &arguments[i]

4) Для получения результатов потоков запускается функция pthread_join(thread[i], NULL) по количеству потоков. После чего можно получить результат.

Ниже приведена программа вычисляющая сумму первых 100 миллионов чисел. Количество потоков (используемых ядер процессора) может задаваться переменной THREAD_SIZE (от 1 до, например, 100)


#include <stdio.h> //printf()
#include <time.h> // clock()
#include <pthread.h> // pthread_create(), pthread_join()
const int THREAD_SIZE = 8; // Количество потоков
const int ELEMENT_SIZE = 100000000; // Количество элементов для расчёта
struct thread_args{ // Объявляем структуру для передачи в поток
    int index_start; // Начальный индекс данных
    int index_end; // Конечный индекс данных
    double res; // Результат
};
void * thread_func(void *args){ // Функция потока, занимается вычислениями
    struct thread_args *st_args = (struct thread_args *)args; // Преобразуем аргумент в указатель на структуру
    double res = 0; // Результат вычислений
    for(int i = st_args->index_start; i < st_args->index_end; i++){ // Перебираем элементы
        res += i; // Суммируем числа
    }
    st_args->res = res;
    return NULL;
}

int main(){
    pthread_t thread[THREAD_SIZE];   // Идентификаторы потоков
    struct thread_args arguments[THREAD_SIZE]; // Массив структур аргументов для передачи в потоки
    int data_size = ELEMENT_SIZE / THREAD_SIZE; // Размер данных обрабатываемых потоком
    for(int i = 0; i < THREAD_SIZE; i++){ // Перебираем и создаем потоки
        arguments[i].index_start = data_size * i; // Начальный индекс данных
        arguments[i].index_end = data_size * (i + 1); // Конечный индекс данных
        pthread_create(&thread[i], NULL, thread_func, &arguments[i]);  // Запуск потока
    }
    double result = 0; // Итоговый результат
    for(int i = 0; i < THREAD_SIZE; i++){
        pthread_join(thread[i], NULL);  // Ожидаем завершение потока
        result += arguments[i].res;  // Обрабатываем результаты от выполнения потока (суммируем данные от каждого потока)
    }
    unsigned int time = clock(); // время с момента запуска программы (мс)
    printf("res = %f \ntime = %d ms \n", result, time); // Выводим результат и время выполнения программы в мс.
}

Результаты выполнения программы на ноутбуке с i7-7700HQ (4 ядра, 8 потоков). Меньше время выполнения (time в миллисекундах) - лучше. THREAD_SIZE - число используемых потоков
THREAD_SIZE = 1, time = 476 ms
THREAD_SIZE = 2, time = 239 ms
THREAD_SIZE = 4, time = 125 ms
THREAD_SIZE = 8, time = 85 ms
THREAD_SIZE = 10, time = 100 ms (Дальнейший рост потоков не дает уменьшения времени выполнения)
Результат: использование 8-ми потоков ускорило выполнение программы в 5,6 раза.
2023-03-12



Понравилась страница?
Добавить в закладки
Или поделиться!

Связанные темы

Многопоточность в языке Си реализованная функциями pthread_create(), pthread_join()