В этой статье поговорим о параллельных вычислениях, реализуемых средствами Windows API.
В контексте исполнения процесса могут выполняться несколько потоков. В операционной системе Windows поток — это единица исполнения, которой ОС выделяет процессорное время для выполнения программы.
Рассмотрим функций доступные в WIndows API для работы с потоками. Для того, чтобы создать поток используется функция CreateThread.
1 2 3 4 5 6 7 8 |
HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpSecAttr, SIZE_T StackSize, LPTHREAD_START_ROUTINE lpStartFuncAddr, LPVOID p, DWORD dwCreatParam, LPDWORD thrId ); |
где:
- lpSecAttr – указатель на SECURITY_ATTRIBUTES, этот параметр также можно указать равным NULL, тогда возвращаемый дескриптор не будет наследоваться
- StackSize – начальный размер стека (указывается в байтах). Если в качестве параметра указать ноль, то система задаст размер стека по умолчанию
- lpStartFuncAddr – это указатель на функцию, которая будет исполняться потоком, таким образом здесь будет указан стартовый адрес потока; эта функция должна быть определена в программе следующим образом: DWORD WINAPI FunctionName(LPVOID)
- p – это указатель на переменную, которую нам необходимо передать в функцию FunctionName(LPVOID p), исполняемую потоком
- dwCreatParam – параметры, которые управляют созданием потока. Можно указать: 0 (тогда поток начнет исполняться сразу после его создания), CREATE_SUSPENDED (поток начнет исполняться, когда будет выполнена функция ResumeThread, о ней позже)
- thrId – это указатель на переменную, в которую будет записан идентификатор потока
ExitThread — эта функция завершает поток, как видно из её определения ниже, она ничего не возвращает. Поток может завершиться при вызове этой функции или при возврате из функции DWORD WINAPI FunctionName(LPVOID), которая исполнялась в потоке.
1 |
VOID WINAPI ExitThread(DWORD dwExitCode); |
- в dwExitCode помещаем код завершения потока
Чтобы приостановить поток, нужно использовать функцию — SuspendThread. В случае успешной остановки потока функция вернёт предыдущее число остановок данного потока, есть исполнение функции SuspendThread завершится неудачей, то она вернёт -1.
1 |
DWORD WINAPI SuspendThread(HANDLE thread); |
- thread — дескриптор потока, который необходимо приостановить. Он должен иметь права доступа: THREAD_SUSPEND_RESUME
Если нужно возобновить данный поток, то необходимо вызвать функцию ResumeThread. При успешном выполнении возвращается предыдущее количество остановок потока, иначе -1.
1 |
DWORD WINAPI ResumeThread(HANDLE thread); |
- thread — дескриптор потока, который необходимо возобновить. Он должен иметь права доступа: THREAD_SUSPEND_RESUME
Теперь рассмотрим пример программы, которая осуществляет параллельную многопоточную генерацию и обработку строк двумерной матрицы. То есть каждый отдельный поток будет генерировать строку матрицы и находить произведение нечётных элементов этой строки. В каждый поток будем передавать в качестве параметра указатель на структуру, в которой будет хранится строка матрицы и результат произведения элементов.
Подключим необходимые библиотеки: для вывода результатов на экран, для использования функций WinAPI и для использования времени в качестве инициализирующего элемента генератора случайных чисел.
1 2 3 |
#include <stdio.h> #include <Windows.h> #include <time.h> |
Определим константу для хранения размера матрицы (в данном примере матрица размером 4×4).
1 |
#define MATRIX_SIZE 4 |
Определим структуру, в которой будем хранить строку матрицы, результат умножения нечётных элементов этой строки и случайное число, которым будет инициализирован генератор случайных чисел в данном потоке (если использовать один и тот же генератор для всех потоков, то, из-за одновременности выполнения, будут генерироваться одинаковые числа для всех строк).
1 2 3 4 5 6 |
struct row { int value[MATRIX_SIZE]; int result; int rnd; }; |
Функция DWORD WINAPI generateAndCalc(void *data), которую будут исполнять потоки (с комментариями):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
DWORD WINAPI generateAndCalc(void *data) { //преобразуем полученные данные к типу структуры row *r = (row *) data; //инициализируем генератор случайных чисел полученным числом srand(r->rnd); //генерируем элементы строки for (int i = 0; i < MATRIX_SIZE; i++) { r->value[i] = rand() % 10; } //находим произведение нечетных элементов r->result = 1; for (int i = 0; i < MATRIX_SIZE; i += 2) { r->result *= r->value[i]; } return 0; } |
Функция main() с комментариями:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
int main() { //инициализируем генератор случайных чисел srand(time(NULL)); //определяем дескрипторы потоков, //идентификаторы потоков и структуры для строк матрицы HANDLE thread[MATRIX_SIZE]; DWORD thrId[MATRIX_SIZE]; row rows[MATRIX_SIZE]; for (int i = 0; i < MATRIX_SIZE; i++) { //генерируем случайные числа для каждой строки rows[i].rnd = rand(); //создаем потоки thread[i] = CreateThread(NULL, 0, &generateAndCalc, &rows[i], 0, &thrId[i]); } //ждем, пока все эти потоки завершатся WaitForMultipleObjects(MATRIX_SIZE, thread, TRUE, INFINITE); //выводим результат работы программы на экран for (int i = 0; i < MATRIX_SIZE; i++) { for (int j = 0; j < MATRIX_SIZE; j++) { printf(" %d", rows[i].value[j]); } printf(" | multipl = %d\n", rows[i].result); } return 0; } |
Работа программы демонстрируется на скриншоте ниже:
Скачать исходник
Поделиться в соц. сетях:
С помощью какой версии был написан исходник?
Visual Studio 2012
Спасибо.
Почему страница на исходник недоступная?
Только что проверил — все работает. Если у Вас возникают проблемы со скачиванием, напишите нам в «Обратную связь» — пришлем Вам исходник.