Win API Многопоточность: ReleaseSemaphore не изменяет счётчик
Я хочу создать массив массивов (std::vector<std::vector<uint32_t>> arrays), каждый из них заполнить числами. Также должен быть массив Win API потоков (std::vector<HANDLE> threads). Потоков должно быть вдвое больше, чем массивов (в коде ниже массивов 2, а потоков 4). Мне нужно использовать семафоры для синхронизации. Что я хочу реализовать: сначала сортируются 2 массива, но создаются сразу 4 потока, 3 и 4 потоки останавливаются перед функцией WaitForSingleObject(hSemaphore, INFINITE), потому что значение счётчика обнуляется (создаю семафор так: CreateSemaphoreW(nullptr, arrays.size(), threads.size(), nullptr)). Я помещаю эту функцию в отдельный поток manage_thread по следующей причине (установлено экспериментально): в функцию sort_arr поток, сортирующий 3-й массив, может "попасть" раньше потока, сортирующего 1-й, в то время как в arrays всего 2 массива. Пока 3 и 4 потоки ждут, главный поток вставляет 3 и 4 массивы, после чего освобождает hSemaphore, увеличивая счётчик и пропуская потоки. 3 и 4 потоки сортируют 3 и 4 массивы.
Но что получается у меня: программа не завершается, останавливаясь в цикле функции manage_threads при i = 2. Я подозреваю, что неправильно использую ReleaseSemaphore(hSemaphore, arrays.size(), nullptr), но, к сожалению, не могу самостоятельно найти ошибку :(
#include <Windows.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>
std::vector<std::vector<uint32_t>> arrays(2);
HANDLE hSemaphore;
std::vector<HANDLE> threads(arrays.size() * 2);
DWORD WINAPI sort_arr(LPVOID lpParam) {
auto i = reinterpret_cast<size_t>(lpParam);
decltype(auto) arr = arrays[i];
std::ranges::sort(arr);
return EXIT_SUCCESS;
}
DWORD WINAPI manage_threads(LPVOID) {
for (size_t i = 0; i < threads.size(); ++i) {
WaitForSingleObject(hSemaphore, INFINITE);
threads[i] = CreateThread(nullptr, 0, sort_arr, (LPVOID)(i), 0, nullptr);
}
WaitForMultipleObjects(threads.size(), threads.data(), true, INFINITE);
return EXIT_SUCCESS;
}
int main() {
const auto max = 10u;
const auto range = std::views::iota(1u, max + 1) | std::views::reverse;
const auto print_arr = [](const auto& arr) { for (auto i : arr) std::cout << i << ' '; std::cout << '\n'; };
for (auto& arr : arrays)
std::ranges::copy(range.begin(), range.end(), std::back_inserter(arr));
hSemaphore = CreateSemaphoreW(nullptr, arrays.size(), threads.size(), nullptr);
auto manage_thread = CreateThread(nullptr, 0, manage_threads, nullptr, 0, nullptr);
for (size_t i = arrays.size(); i < threads.size(); ++i) {
arrays.emplace_back();
std::ranges::copy(range.begin(), range.end(), std::back_inserter(arrays[i]));
}
ReleaseSemaphore(hSemaphore, arrays.size(), nullptr);
WaitForSingleObject(manage_thread, INFINITE);
std::ranges::for_each(arrays, print_arr);
}
Ответы (1 шт):
Спасибо всем, кто обратил внимание на мой вопрос! Вот исправленный код:
#include <Windows.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>
std::vector<std::vector<uint32_t>> arrays(10);
HANDLE hSemaphore;
std::vector<HANDLE> threads(arrays.size() * 2);
DWORD WINAPI sort_arr(LPVOID lpParam) {
auto i = reinterpret_cast<size_t>(lpParam);
decltype(auto) arr = arrays[i];
std::ranges::sort(arr);
return EXIT_SUCCESS;
}
DWORD WINAPI manage_threads(LPVOID) {
for (size_t i = 0; i < threads.size(); ++i) {
WaitForSingleObject(hSemaphore, INFINITE);
threads[i] = CreateThread(nullptr, 0, sort_arr, (LPVOID)(i), 0, nullptr);
}
WaitForMultipleObjects(threads.size(), threads.data(), true, INFINITE);
return EXIT_SUCCESS;
}
int main() {
const auto max = 10u;
const auto range = std::views::iota(1u, max + 1) | std::views::reverse;
const auto print_arr = [](const auto& arr) { for (auto i : arr) std::cout << i << ' '; std::cout << '\n'; };
for (auto& arr : arrays)
std::ranges::copy(range.begin(), range.end(), std::back_inserter(arr));
hSemaphore = CreateSemaphoreW(nullptr, arrays.size(), threads.size(), nullptr);
auto manage_thread = CreateThread(nullptr, 0, manage_threads, nullptr, 0, nullptr);
for (size_t i = arrays.size(); i < threads.size(); ++i) {
arrays.emplace_back();
std::ranges::copy(range.begin(), range.end(), std::back_inserter(arrays[i]));
}
ReleaseSemaphore(hSemaphore, arrays.size() / 2, nullptr);
WaitForSingleObject(manage_thread, INFINITE);
std::ranges::for_each(arrays, print_arr);
}
Проблема была действительно в вызове функции ReleaseSemaphore. До исправлений она увеличивала счётчик на array.size(), после исправлений стала увеличивать счётчик на array.size() / 2. Я пока ещё не понимаю, каким образом организована работа со счётчиком внутри семафоров, поэтому не могу представить, почему предыдущий вариант не сработал. Как это выглядит в моей голове: счётчик остановлен на 0, после ReleaseSemaphore(hSemaphore, arrays.size(), nullptr) он равен 4, остальные потоки спокойно проходят. Но действительность отличается...