Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> Язык C

Освоение многопоточности в C++: эффективное параллельное программирование

Многопоточность — это специализированная форма многозадачности. Многозадачность позволяет компьютеру запускать две или более программ одновременно. Существует два типа многозадачности: основанная на процессах и основанная на потоках.

Многопоточная программа содержит два или более потока, каждый из которых определяет отдельный путь выполнения. В C++ нет встроенной поддержки многопоточности — она полностью полагается на ОС. В этом руководстве мы используем POSIX Threads (Pthreads) на Linux. Pthreads доступны во многих Unix-подобных системах: FreeBSD, NetBSD, GNU/Linux, macOS, Solaris.

Создание потоков

Для создания потока POSIX используется функция pthread_create:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void *), void *arg);

Параметры:

№Параметр и описание
1thread — уникальный идентификатор нового потока, возвращаемый подпрограммой.
2attr — объект атрибутов потока (или NULL для значений по умолчанию).
3start_routine — функция, которую выполнит поток после создания.
4arg — единственный аргумент, передаваемый в start_routine (указатель void*). NULL, если аргумент не нужен.

Максимальное число потоков зависит от реализации. После создания потоки равноправны и могут создавать другие потоки; иерархии нет.

Завершение потоков

Для явного завершения потока используется pthread_exit:

#include <pthread.h>
void pthread_exit(void *status);

Обычно вызывается после завершения работы потока. Если main() завершается раньше созданных потоков и вызывает pthread_exit(), остальные потоки продолжают выполняться. Иначе они автоматически завершаются при выходе из main().

Пример: создание 5 потоков

#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;

#define NUM_THREADS 5

void *PrintHello(void *threadid) {
    long tid = (long)threadid;
    cout << "Hello World! Thread ID, " << tid << endl;
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    int rc;
    for (int i = 0; i < NUM_THREADS; i++) {
        cout << "main() : creating thread, " << i << endl;
        rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);
        if (rc) {
            cout << "Error: unable to create thread, " << rc << endl;
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

Компиляция: g++ test.cpp -lpthread

Вывод:

main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Hello World! Thread ID, 0
Hello World! Thread ID, 1
Hello World! Thread ID, 2
Hello World! Thread ID, 3
Hello World! Thread ID, 4

Передача аргументов в потоки

Можно передать несколько аргументов через структуру. Указатель void* позволяет передать любой тип данных.

#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;

#define NUM_THREADS 5

struct thread_data {
    int thread_id;
    char *message;
};

void *PrintHello(void *threadarg) {
    struct thread_data *my_data = (struct thread_data *) threadarg;
    cout << "Thread ID : " << my_data->thread_id;
    cout << " Message : " << my_data->message << endl;
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    struct thread_data td[NUM_THREADS];
    int rc;
    for (int i = 0; i < NUM_THREADS; i++) {
        cout << "main() : creating thread, " << i << endl;
        td[i].thread_id = i;
        td[i].message = "This is message";
        rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);
        if (rc) {
            cout << "Error: unable to create thread, " << rc << endl;
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

Объединение (join) и отсоединение (detach) потоков

Две функции для синхронизации:

pthread_join(threadid, status)
pthread_detach(threadid)

pthread_join() блокирует вызывающий поток до завершения указанного потока. Поток должен быть создан как joinable (по умолчанию). Отсоединённые потоки нельзя присоединить.

Пример с pthread_join

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;

#define NUM_THREADS 5

void *wait(void *t) {
    long tid = (long)t;
    sleep(1);
    cout << "Sleeping in thread " << endl;
    cout << "Thread with id : " << tid << " ...exiting " << endl;
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    pthread_attr_t attr;
    void *status;
    // инициализация атрибутов и установка joinable
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    for (int i = 0; i < NUM_THREADS; i++) {
        cout << "main() : creating thread, " << i << endl;
        int rc = pthread_create(&threads[i], &attr, wait, (void *)i);
        if (rc) {
            cout << "Error: unable to create thread, " << rc << endl;
            exit(-1);
        }
    }
    // освобождение атрибутов и ожидание завершения потоков
    pthread_attr_destroy(&attr);
    for (int i = 0; i < NUM_THREADS; i++) {
        int rc = pthread_join(threads[i], &status);
        if (rc) {
            cout << "Error: unable to join, " << rc << endl;
            exit(-1);
        }
        cout << "Main: completed thread id :" << i;
        cout << " exiting with status :" << status << endl;
    }
    cout << "Main: program exiting." << endl;
    pthread_exit(NULL);
}

Вывод демонстрирует последовательное завершение потоков и ожидание главного потока.

Язык C

  1. Освоение операторов C++: полное руководство с практическими примерами
  2. Комментарии в C++: лучшие практики для читаемого и поддерживаемого кода
  3. Шаблоны классов в C++: практическое руководство
  4. Язык программирования C++: обзор и ключевые особенности
  5. Константы и литералы в C++: типы, синтаксис и лучшие практики
  6. Полное руководство по операторам C++: типы, использование и лучшие практики
  7. Работа с числовыми типами в C++: практическое руководство
  8. Ссылки в C++: ключевые концепции и отличия от указателей
  9. Шаблоны в C++: руководство по обобщённому программированию
  10. Освоение многопоточности в C++: эффективное параллельное программирование