C ++ Многопоточность
Многопоточность - это специализированная форма многозадачности, а многозадачность - это функция, которая позволяет вашему компьютеру одновременно запускать две или более программы. В общем, существует два типа многозадачности: основанная на процессе и основанная на потоках.
Многозадачность, основанная на процессах, обеспечивает одновременное выполнение программ. Многозадачность на основе потоков имеет дело с одновременным выполнением частей одной и той же программы.
Многопоточная программа состоит из двух или более частей, которые могут выполняться одновременно. Каждая часть такой программы называется потоком, и каждый поток определяет отдельный путь выполнения.
C ++ не содержит встроенной поддержки многопоточных приложений. Вместо этого он полностью полагается на операционную систему, чтобы обеспечить эту функцию.
В этом руководстве предполагается, что вы работаете в ОС Linux, и мы собираемся написать многопоточную программу на C ++ с использованием POSIX. POSIX Threads или Pthreads предоставляют API, которые доступны во многих Unix-подобных системах POSIX, таких как FreeBSD, NetBSD, GNU / Linux, Mac OS X и Solaris.
Создание потоков
Следующая процедура используется для создания потока POSIX -
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
Вот, pthread_createсоздает новый поток и делает его исполняемым. Эту процедуру можно вызывать любое количество раз из любого места в вашем коде. Вот описание параметров -
Старший Нет | Параметр и описание |
---|---|
1 | thread Непрозрачный уникальный идентификатор нового потока, возвращаемый подпрограммой. |
2 | attr Непрозрачный объект атрибута, который может использоваться для установки атрибутов потока. Вы можете указать объект атрибутов потока или NULL для значений по умолчанию. |
3 | start_routine Подпрограмма C ++, которую поток будет выполнять после создания. |
4 | arg Единственный аргумент, который можно передать start_routine. Он должен передаваться по ссылке как приведение указателя типа void. Если аргумент не передается, можно использовать NULL. |
Максимальное количество потоков, которое может быть создано процессом, зависит от реализации. После создания потоки являются одноранговыми и могут создавать другие потоки. Между потоками нет подразумеваемой иерархии или зависимости.
Завершение потоков
Существует следующая процедура, которую мы используем для завершения потока POSIX:
#include <pthread.h>
pthread_exit (status)
Вот pthread_exitиспользуется для явного выхода из потока. Обычно процедура pthread_exit () вызывается после того, как поток завершил свою работу и больше не требуется для существования.
Если main () завершается раньше, чем потоки, которые он создал, и завершается с помощью pthread_exit (), другие потоки продолжат выполнение. В противном случае они будут автоматически завершены после завершения main ().
Example
Этот простой пример кода создает 5 потоков с помощью процедуры pthread_create (). Каждая ветка печатает «Hello World!» сообщение, а затем завершается вызовом pthread_exit ().
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void *PrintHello(void *threadid) {
long tid;
tid = (long)threadid;
cout << "Hello World! Thread ID, " << tid << endl;
pthread_exit(NULL);
}
int main () {
pthread_t threads[NUM_THREADS];
int rc;
int i;
for( 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);
}
Скомпилируйте следующую программу с помощью библиотеки -lpthread следующим образом:
$gcc 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;
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;
int i;
for( 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);
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 2 Message : This is message
Thread ID : 0 Message : This is message
Thread ID : 1 Message : This is message
Thread ID : 4 Message : This is message
Присоединение и отсоединение потоков
Есть следующие две процедуры, которые мы можем использовать для соединения или отсоединения потоков:
pthread_join (threadid, status)
pthread_detach (threadid)
Подпрограмма pthread_join () блокирует вызывающий поток до тех пор, пока указанный поток 'threadid' не завершится. Когда создается поток, один из его атрибутов определяет, может ли он присоединиться или отсоединиться. Можно присоединить только те потоки, которые созданы как присоединяемые. Если поток создается как отсоединенный, он никогда не может быть присоединен.
В этом примере показано, как дождаться завершения потока с помощью процедуры соединения Pthread.
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void *wait(void *t) {
int i;
long tid;
tid = (long)t;
sleep(1);
cout << "Sleeping in thread " << endl;
cout << "Thread with id : " << tid << " ...exiting " << endl;
pthread_exit(NULL);
}
int main () {
int rc;
int i;
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;
// Initialize and set thread joinable
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for( i = 0; i < NUM_THREADS; i++ ) {
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], &attr, wait, (void *)i );
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
// free attribute and wait for the other threads
pthread_attr_destroy(&attr);
for( i = 0; i < NUM_THREADS; i++ ) {
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);
}
Когда приведенный выше код компилируется и выполняется, он дает следующий результат:
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread
Thread with id : 0 .... exiting
Sleeping in thread
Thread with id : 1 .... exiting
Sleeping in thread
Thread with id : 2 .... exiting
Sleeping in thread
Thread with id : 3 .... exiting
Sleeping in thread
Thread with id : 4 .... exiting
Main: completed thread id :0 exiting with status :0
Main: completed thread id :1 exiting with status :0
Main: completed thread id :2 exiting with status :0
Main: completed thread id :3 exiting with status :0
Main: completed thread id :4 exiting with status :0
Main: program exiting.