The task is to write a c++ program that creates 4 threads, that each of them loops a 1000 times, and each time prints his thread-id and the loop count. All 4 threads work separately.
- This program must use the WINAPI functions.
- The program must use the RAII concept.
As a starter, I have written a simple answer without RAII that seems to work properly:
#include <iostream> #include <Windows.h> #include <vector> constexpr int THREAD_COUNT = 4; constexpr int LOOP_COUNT = 1000; DWORD WINAPI ThreadFunction(LPVOID lpParam) { int threadId = reinterpret_cast<INT_PTR>(lpParam); for (int i = 0; i < LOOP_COUNT; ++i) { std::cout << "Thread " << threadId << ", var = " << i << std::endl; } return 0; } int main() { std::vector<HANDLE> threadHandles; for (int i = 1; i <= THREAD_COUNT; ++i) { HANDLE hThread = CreateThread(NULL, 0, ThreadFunction, reinterpret_cast<LPVOID>(i), 0, NULL); if (hThread == NULL) { std::cerr << "Error creating thread " << i << std::endl; return 1; } threadHandles.push_back(hThread); } DWORD dwWaitResult = WaitForMultipleObjects(THREAD_COUNT, threadHandles.data(), TRUE, INFINITE); if (dwWaitResult != WAIT_OBJECT_0) { std::cerr << "WaitForMultipleObjects failed: " << GetLastError() << std::endl; return 1; } for (HANDLE hThread : threadHandles) { CloseHandle(hThread); } return 0; } Yet, when trying to add RAII, something doesn't quite work (the threads randomly stop at the middle of the iteration):
ThreadHandle.h
#pragma once #include <windows.h> class ThreadHandle { public: ThreadHandle() = default; ThreadHandle(LPTHREAD_START_ROUTINE function, LPVOID lpParameter); ~ThreadHandle(); operator HANDLE() const { return hThread; } private: HANDLE hThread; }; ThreadHandle.cpp
#include <iostream> #include "ThreadHandle.h" ThreadHandle::ThreadHandle(LPTHREAD_START_ROUTINE function, LPVOID lpParameter) : hThread(nullptr) { hThread = CreateThread( NULL, 0, function, lpParameter, 0, NULL ); if (hThread == NULL) { throw std::runtime_error("Create thread failed"); } } ThreadHandle::~ThreadHandle() { if (hThread == nullptr) { return; } CloseHandle(hThread); } main.cpp
#include <vector> #include <iostream> #include "ThreadHandle.h" #define THREAD_COUNT 4 #define LOOP_COUNT 1000 DWORD WINAPI LoopThousand(LPVOID lpParam) { int threadId = reinterpret_cast<int>(lpParam); for (int i = 0; i < LOOP_COUNT; i++) { std::cout << "Thread " << threadId << ", var = " << i << std::endl; } return 0; } int main() { std::vector<HANDLE> handles; try { for (int i = 1; i <= THREAD_COUNT; i++) { ThreadHandle hThread(LoopThousand, reinterpret_cast<LPVOID>(i)); handles.push_back(hThread); } if (WaitForMultipleObjects( THREAD_COUNT, handles.data(), TRUE, INFINITE ) == WAIT_FAILED) throw std::runtime_error("Wait for multiple objects failed"); } catch (std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; } Would like some help on the matter (and feel free to give corrections on the given code).
push_backcall creates a copy ofhThread, and then the original is destroyed. This results in two copies ofThreadHandlemanaging the same thread, one of which immediately closes the thread handle. Construct in-place withemplace_back, or better still implement a move constructor and delete copy constructor/assignment.ThreadHandleshould be non-copyable too. Or make it ref counting (you can makeThreadHandlehave shared pointer to aThreadHandleImplas member to get the ref counting for free)