1

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).

4
  • 3
    This is a Rule of three / five violation. The push_back call creates a copy of hThread, and then the original is destroyed. This results in two copies of ThreadHandle managing the same thread, one of which immediately closes the thread handle. Construct in-place with emplace_back, or better still implement a move constructor and delete copy constructor/assignment. Commented Apr 15, 2024 at 7:12
  • 2
    threads are non-copyable. Your ThreadHandle should be non-copyable too. Or make it ref counting (you can make ThreadHandle have shared pointer to a ThreadHandleImpl as member to get the ref counting for free) Commented Apr 15, 2024 at 8:34
  • @paddy Ok, so I declared the copy constructor and copy assignment operator as private without giving a definition. I understand that the problem is me constructing a ThreadHandle object, and then copying it with the push_back. Yet, if I try to have a one-liner of creating a ThreadHandle object and pushing it to the array with emplace_back, I can only do so to a ThreadHandle array. And then - I have a problem when calling WaitForMultipleObjects and need to supply a pointer to a HANDLE array (yet I have a pointer to a ThreadHandle array). Not really sure yet how to solve it. Commented Apr 15, 2024 at 9:43
  • @Torch Alternatively, you can delete the constructors. Keep in mind there is also a move constructor that has specific rules. Commented Apr 17, 2024 at 11:17

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.