{"id":21274182,"url":"https://github.com/endurodave/threadwin","last_synced_at":"2025-06-11T23:06:01.160Z","repository":{"id":113550080,"uuid":"108660147","full_name":"endurodave/ThreadWin","owner":"endurodave","description":"Win32 CreateThread() C++ Wrapper with Synchronized Thread Startup","archived":false,"fork":false,"pushed_at":"2023-01-01T01:09:01.000Z","size":21,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-15T12:45:13.638Z","etag":null,"topics":["cpp","multithreading","threads","win32"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/endurodave.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-28T15:21:35.000Z","updated_at":"2024-12-12T11:48:16.000Z","dependencies_parsed_at":"2023-03-12T12:45:41.516Z","dependency_job_id":null,"html_url":"https://github.com/endurodave/ThreadWin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/endurodave/ThreadWin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FThreadWin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FThreadWin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FThreadWin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FThreadWin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/endurodave","download_url":"https://codeload.github.com/endurodave/ThreadWin/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FThreadWin/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259360806,"owners_count":22845824,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","multithreading","threads","win32"],"created_at":"2024-11-21T09:19:16.165Z","updated_at":"2025-06-11T23:06:01.155Z","avatar_url":"https://github.com/endurodave.png","language":"C++","readme":"# Win32 Thread Wrapper with Synchronized Start\nA Win32 CreateThread() C++ wrapper class for synchronized thread startup and forced message queue creation.\n\nOriginally published on CodeProject at: \u003ca href=\"https://www.codeproject.com/Articles/1095196/Win-Thread-Wrapper-with-Synchronized-Start\"\u003e\u003cstrong\u003eWin32 Thread Wrapper with Synchronized Start\u003c/strong\u003e\u003c/a\u003e\n\n\u003ch2\u003eIntroduction\u003c/h2\u003e\n\n\u003cp\u003eAll operating systems provide services to create threads or tasks. On a Windows Win32 application, the main API is \u003ccode\u003eCreateThread()\u003c/code\u003e. While it\u0026rsquo;s possible to use the raw Win32 functions, I find it better to encapsulate the behavior into a class that enforces the correct behaviors.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eOn multithreaded systems, you sometimes need a synchronized startup of all the threads. Depending on the design, if a worker thread starts processing too soon before other threads have had a chance to initialize\u0026nbsp;it can cause problems. Therefore, creating all threads first and simultaneously starting them with a synchronizing event solves this problem.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe Win32 thread API also has idiosyncrasies that, if not managed, can cause intermittent failures at runtime. One problem revolves around the message queue and when it\u0026rsquo;s created. After \u003ccode\u003eCreateThread()\u003c/code\u003e is called the message queue isn\u0026rsquo;t initialized immediately. The new thread needs a chance to run first. A \u003ccode\u003ePostThreadMessage()\u003c/code\u003e to a thread without a message queue causes the function to fail.\u003c/p\u003e\n\n\u003cp\u003eFor these reasons, when designing a Win32 application it\u0026rsquo;s beneficial to use an encapsulating class to enforce correct behavior and prevent runtime errors.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eMany wrapper class implementations exist for Win32 threads. However, I found none that solves the aforementioned problems. The \u003ccode\u003eThreadWin \u003c/code\u003eclass provided here has the following benefits.\u0026nbsp;\u003c/p\u003e\n\n\u003col\u003e\n\t\u003cli\u003e\u003cstrong\u003eSynchronized start\u003c/strong\u003e \u0026ndash; start all created threads simultaneously using a synchronization event.\u0026nbsp;\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eForce create queue\u003c/strong\u003e \u0026ndash; force creating the thread message queue to prevent runtime errors.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eEntry and exit\u003c/strong\u003e \u0026ndash; manage the thread resources and provide orderly startup and exit.\u0026nbsp;\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003eEase of use\u003c/strong\u003e \u0026ndash; implement a single function Process() for a thread loop.\u0026nbsp;\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003ch2\u003eUsing the Code\u003c/h2\u003e\n\n\u003cp\u003e\u003ccode\u003eThreadWin\u003c/code\u003e provides the Win32 thread encapsulation. The constructor allows naming the thread and controlling whether synchronized startup is desired.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nclass ThreadWin \n{\npublic:\n    ThreadWin (const CHAR* threadName, BOOL syncStart = TRUE);\n    // \u0026hellip;\n};\u003c/pre\u003e\n\n\u003cp\u003eInherit from the class and implement the pure virtual function \u003ccode\u003eProcess()\u003c/code\u003e.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nvirtual unsigned long Process (void* parameter) = 0;\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003eWorkerThread\u003c/code\u003e has a simple message loop and shows how to inherit from \u003ccode\u003eThreadWin\u003c/code\u003e.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nclass WorkerThread : public ThreadWin\n{\npublic:\n    WorkerThread(const CHAR* threadName) : ThreadWin(threadName) {}\n\nprivate:\n    /// The worker thread entry function\n    virtual unsigned long Process (void* parameter)\n    {\n        MSG msg;\n        BOOL bRet;\n        while ((bRet = GetMessage(\u0026amp;msg, NULL, WM_USER_BEGIN, WM_USER_END)) != 0)\n        {\n            switch (msg.message)\n            {\n                case WM_THREAD_MSG:\n                {\n                    ASSERT_TRUE(msg.wParam != NULL);\n\n                    // Get the ThreadMsg from the wParam value\n                    ThreadMsg* threadMsg = reinterpret_cast\u0026lt;ThreadMsg*\u0026gt;(msg.wParam);\n\n                    // Print the incoming message\n                    cout \u0026lt;\u0026lt; threadMsg-\u0026gt;message.c_str() \u0026lt;\u0026lt; \u0026quot; \u0026quot; \u0026lt;\u0026lt; GetThreadName() \u0026lt;\u0026lt; endl;\n\n                    // Delete dynamic data passed through message queue\n                    delete threadMsg;\n                    break;\n                }\n\n                case WM_EXIT_THREAD:\n                    return 0;\n\n                default:\n                    ASSERT();\n            }\n        }\n        return 0;\n    }\n\n};\u003c/pre\u003e\n\n\u003cp\u003eCreating thread objects is easy.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nWorkerThread workerThread1(\u0026quot;WorkerThread1\u0026quot;);\nWorkerThread workerThread2(\u0026quot;WorkerThread2\u0026quot;);\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003eCreateThread()\u003c/code\u003e is used to create the thread and forces the message queue to be created. The thread is now waiting for the startup synchronization event before entering the \u003ccode\u003eProcess()\u003c/code\u003e message loop.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nworkerThread1.CreateThread();\nworkerThread2.CreateThread();\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003eThreadWin::StartAllThreads()\u003c/code\u003e starts all system threads at once. The threads are now allowed to enter the \u003ccode\u003eProcess()\u003c/code\u003e message loop.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nThreadWin::StartAllThreads();\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003ePostThreadMessage()\u003c/code\u003e sends data to the worker thread.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\n// Create message to send to worker thread 1\nThreadMsg* threadMsg = new ThreadMsg();\nthreadMsg-\u0026gt;message = \u0026quot;Hello world!\u0026quot;;\n\n// Post the message to worker thread 1\nworkerThread1.PostThreadMessage(WM_THREAD_MSG, threadMsg);\u003c/pre\u003e\n\n\u003cp\u003eUse\u003ccode\u003e ExitThread()\u003c/code\u003e for an orderly thread exit and cleanup used resources.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nworkerThread1.ExitThread();\nworkerThread2.ExitThread();\u003c/pre\u003e\n\n\u003ch2\u003eImplementation\u003c/h2\u003e\n\n\u003cp\u003e\u003ccode\u003eThreadWin::CreateThread() \u003c/code\u003ecreates a thread using the Win32 \u003ccode\u003eCreateThread()\u003c/code\u003e API. The main thread entry function is \u003ccode\u003eThreadWin::RunProcess()\u003c/code\u003e. After the thread is created, the call blocks waiting for the thread to finish creating the message queue using \u003ccode\u003eWaitForSingleObject(m_hThreadStarted, MAX_WAIT_TIME)\u003c/code\u003e.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nBOOL ThreadWin::CreateThread()\n{\n\u0026nbsp;\u0026nbsp; \u0026nbsp;// Is the thread already created?\n\u0026nbsp;\u0026nbsp; \u0026nbsp;if (!IsCreated ())\u0026nbsp;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;{\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;m_hThreadStarted = CreateEvent(NULL, TRUE, FALSE, TEXT(\u0026quot;ThreadCreatedEvent\u0026quot;));\u0026nbsp;\u0026nbsp; \u0026nbsp;\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;// Create the worker thread\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;ThreadParam threadParam;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;threadParam.pThread\u0026nbsp;\u0026nbsp; \u0026nbsp;= this;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;m_hThread = ::CreateThread (NULL, 0, (unsigned long (__stdcall *)(void *))RunProcess, (void *)(\u0026amp;threadParam), 0, \u0026amp;m_threadId);\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;ASSERT_TRUE(m_hThread != NULL);\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;// Block the thread until thread is fully initialized including message queue\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;DWORD err = WaitForSingleObject(m_hThreadStarted, MAX_WAIT_TIME);\u0026nbsp;\u0026nbsp; \u0026nbsp;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;ASSERT_TRUE(err == WAIT_OBJECT_0);\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;CloseHandle(m_hThreadStarted);\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;m_hThreadStarted = INVALID_HANDLE_VALUE;\n\n\u0026nbsp;\u0026nbsp; \u0026nbsp;\u0026nbsp;\u0026nbsp; \u0026nbsp;return m_hThread ? TRUE : FALSE;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;}\n\u0026nbsp;\u0026nbsp; \u0026nbsp;return FALSE;\n}\u003c/pre\u003e\n\n\u003cp\u003eThe Microsoft \u003ccode\u003ePostThreadMessasge()\u003c/code\u003e function documentation explains how to force the message queue creation.\u003c/p\u003e\n\n\u003cblockquote class=\"quote\"\u003e\n\u003cdiv class=\"op\"\u003eQuote:\u003c/div\u003e\n\n\u003cp\u003eIn the thread to which the message will be posted, call PeekMessage as shown here to force the system to create the message queue.\u003c/p\u003e\n\n\u003cp\u003ePeekMessage(\u0026amp;msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)\u003c/p\u003e\n\u003c/blockquote\u003e\n\n\u003cp\u003eIf you don\u0026rsquo;t force the queue creation,\u003ccode\u003e PostThreadMessage()\u003c/code\u003e may fail randomly depending on how the threads initialize. You can prove this by creating a thread using \u003ccode\u003eCreateThread()\u003c/code\u003e and immediately post to the new thread using \u003ccode\u003ePostThreadMessage()\u003c/code\u003e. The return value will indicate failure since the thread wasn\u0026rsquo;t given enough time to initialize the message queue. Placing a \u003ccode\u003eSleep(1000)\u003c/code\u003e between\u003ccode\u003e CreateThread()\u003c/code\u003e and \u003ccode\u003ePostThreadMessage()\u003c/code\u003e makes it work, but its fragile. \u003ccode\u003eThreadWin\u003c/code\u003e reliably solves this problem.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003e\u003ccode\u003eThreadWin::RunProcess()\u003c/code\u003e now executes on the new thread of control and forces queue creation using \u003ccode\u003ePeekMessage()\u003c/code\u003e. After queue creation, the waiting\u003ccode\u003e ThreadWin::CreateThread()\u003c/code\u003e function is released using \u003ccode\u003eSetEvent(thread-\u0026gt;m_hThreadStarted)\u003c/code\u003e. If the thread instance wants a synchronized start, it now blocks using \u003ccode\u003eWaitForSingleObject(m_hStartAllThreads, MAX_WAIT_TIME)\u003c/code\u003e.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nint ThreadWin::RunProcess(void* threadParam)\n{\n\u0026nbsp;\u0026nbsp; \u0026nbsp;// Extract the ThreadWin pointer from ThreadParam.\n\u0026nbsp;\u0026nbsp; \u0026nbsp;ThreadWin* thread;\n\u0026nbsp;\u0026nbsp; \u0026nbsp;thread = (ThreadWin*)(static_cast\u0026lt;ThreadParam*\u0026gt;(threadParam))-\u0026gt;pThread;\n\n    // Force the system to create the message queue before setting the event below.\n    // This prevents a situation where another thread calls PostThreadMessage to post\n    // a message before this thread message queue is created.\n    MSG msg;\n    PeekMessage(\u0026amp;msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);\n\n    // Thread now fully initialized. Set the thread started event.\n    BOOL err = SetEvent(thread-\u0026gt;m_hThreadStarted);\n    ASSERT_TRUE(err != 0);\n\n    // Using a synchronized start?\n    if (thread-\u0026gt;SYNC_START == TRUE)\n    {\n        // Block the thread here until all other threads are ready. A call to \n        // StartAllThreads() releases all the threads at the same time.\n        DWORD err = WaitForSingleObject(m_hStartAllThreads, MAX_WAIT_TIME);\n        ASSERT_TRUE(err == WAIT_OBJECT_0);\n    }\n\n    // Call the derived class Process() function to implement the thread loop.\n    int retVal = thread-\u0026gt;Process(NULL);\n\n    // Thread loop exited. Set exit event. \n    err = SetEvent(thread-\u0026gt;m_hThreadExited);\n    ASSERT_TRUE(err != 0);    \n\n    return retVal;\n}\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003eThreadWin::StartAllThreads()\u003c/code\u003e is called to release all waiting threads.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nvoid ThreadWin::StartAllThreads()\n{\n    BOOL err = SetEvent(m_hStartAllThreads);\n    ASSERT_TRUE(err != 0);\n}\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003eThreadWin::ExitThread()\u003c/code\u003e posts a \u003ccode\u003eWM_EXIT_THREAD\u003c/code\u003e to the message queue to exit and waits for the thread to actually exit before returning using \u003ccode\u003eWaitForSingleObject (m_hThreadExited, MAX_WAIT_TIME)\u003c/code\u003e.\u003c/p\u003e\n\n\u003cpre lang=\"c++\"\u003e\nvoid ThreadWin::ExitThread()\n{\n    if (m_hThread != INVALID_HANDLE_VALUE)\n    {\n        m_hThreadExited = CreateEvent(NULL, TRUE, FALSE, TEXT(\u0026quot;ThreadExitedEvent\u0026quot;));    \n\n        PostThreadMessage(WM_EXIT_THREAD);\n\n        // Wait here for the thread to exit\n        if (::WaitForSingleObject (m_hThreadExited, MAX_WAIT_TIME) == WAIT_TIMEOUT)\n            ::TerminateThread (m_hThread, 1);\n\n        ::CloseHandle (m_hThread);\n        m_hThread = INVALID_HANDLE_VALUE;\n\n        ::CloseHandle (m_hThreadExited);\n        m_hThreadExited = INVALID_HANDLE_VALUE;\n    }\n}\u003c/pre\u003e\n\n\u003ch2\u003eConclusion\u003c/h2\u003e\n\n\u003cp\u003e\u003ccode\u003eThreadWin \u003c/code\u003eencapsulates the Win32 thread API in an easy to use class that enforces correct utilization and offers unique thread startup synchronization. Entry and exit features handle all the thread creation and destruction duties. The class standardizes thread usage and reduces common bugs associated with using Win32 worker threads.\u0026nbsp;\u003c/p\u003e\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fthreadwin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendurodave%2Fthreadwin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fthreadwin/lists"}