To achieve the desired behavior in your Win32/MFC application where function A is called once at the start of the first thread, function B is called for each thread, and function C is called by the last remaining thread to finish the job, you can use synchronization primitives such as mutex and condition variables. Here's an example implementation:
cpp
Copy code
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx; // Mutex for synchronization
std::condition_variable cv; // Condition variable for synchronization
bool isFirstThread = true; // Flag to indicate the first thread
int threadCount = 0; // Counter for active threads
void FunctionA()
{
std::cout << "Function A called once." << std::endl;
}
void FunctionB()
{
std::unique_lock<std::mutex> lock(mtx);
// Increment the thread count
threadCount++;
if (isFirstThread)
{
// Call FunctionA for the first thread
FunctionA();
isFirstThread = false;
}
// Call FunctionB for each thread
std::cout << "Function B called by thread." << std::this_thread::get_id() << std::endl;
// Wait until the last thread finishes
cv.wait(lock, [] { return threadCount == 0; });
// Call FunctionC for the last thread
std::cout << "Function C called by the last thread." << std::endl;
}
int main()
{
const int NUM_THREADS = 5;
// Create worker threads
std::vector<std::thread> threads;
for (int i = 0; i < NUM_THREADS; i++)
{
threads.emplace_back(FunctionB);
}
// Wait for threads to finish
for (auto& thread : threads)
{
thread.join();
}
return 0;
}
In this example, we use a mutex (mtx) and a condition variable (cv) to synchronize the threads. The isFirstThread flag is initially set to true, and the threadCount counter keeps track of the active threads.
Each worker thread executes FunctionB(), where it first locks the mutex and increments the thread count. If it's the first thread (isFirstThread is true), it calls FunctionA() and sets isFirstThread to false. Then, it calls FunctionB() for each thread and prints a message indicating the thread ID.
Afterward, the thread waits on the condition variable cv until the threadCount reaches zero. This means that all other threads have finished executing FunctionB(). The lambda function passed to cv.wait() checks if threadCount is zero. When the last thread calls cv.notify_all() (outside the lambda function), all threads waiting on the condition variable are awakened. The last thread proceeds to call FunctionC() and prints a message.
In the main() function, we create NUM_THREADS worker threads and wait for them to finish using join().
Make sure to include the necessary headers (<iostream>, <thread>, <mutex>, <condition_variable>) and link against the appropriate libraries (kernel32.lib, user32.lib, gdi32.lib) for Win32/MFC applications.
Multi threading on Win32, MFC

Hi
In my app (Win32/MFC platform) exist several worker threads and I want according following sample code, function A just called once at starting the fist thread and B called for each thread and C just called by last standing thread to finish the job.
Is my code correct or has some crashes?
Thanks very much!
// I do not write extra codes
LONG g_lThreadsCount = 0;
void Start()
{
// create first worker thread
AfxBeginThread(WorkerThreadProc);
}
void A()
{
// this function MUST called before all other threads just once
// preparing jobs
}
void B()
{
// limit threads count to 8
if (g_lThreadsCount < 8)
AfxBeginThread(WorkerThreadProc);
// doing jobs
}
void C()
{
// finishing jobs
// this function MUST called after all other threads just once
}
UINT WorkerThreadProc(LPVOID pParam)
{
ASSERT(pParam != NULL);
InterlockedIncrement(&g_lThreadsCount);
// if this is fisrt thread, do A just once!
if (g_lThreadsCount == 1)
{
A();
}
B();
// if this is last thread, do C at ending
if (g_lThreadsCount == 1)
{
C();
}
InterlockedDecrement(&g_lThreadsCount);
return 0;
}
C++
3 answers
Sort by: Most helpful
-
Juan Humberto Artero Jaimez 1 Reputation point
2023-06-02T23:38:15.24+00:00 -
drjackool 956 Reputation points
2023-06-03T01:32:36.3833333+00:00 How about this code?!
I invented another way 😎 and think this is solid and full safe! without polling CPU
LONG g_lThreadsRunning = 0; LONG g_lThreadsDone = 0; LONG g_lThreadsFailed = 0; #define MAX_THREADS 8 void Start() { // create first worker thread AfxBeginThread(WorkerThreadProc); } void A() { // preparing jobs } void B() { // I won't trying to recreate failed thread, so add faileds to runnings if (g_lThreadsRunning + g_lThreadsFailed < MAX_THREADS) { if (AfxBeginThread(WorkerThreadProc) == NULL) // also should check resume result InterlockedIncrement(&g_lThreadsFailed); } // doing jobs } void C() { // finishing jobs } UINT WorkerThreadProc(LPVOID pParam) { // if this is first thread, do A just once! if (InterlockedIncrement(&g_lThreadsRunning) == 1) { A(); } B(); // if this is last thread, do C at ending if (g_lThreadsDone + g_lThreadsFailed + 1 == MAX_THREADS) { C(); } InterlockedIncrement(&g_lThreadsDone); return 0; }
-
RLWA32 49,316 Reputation points
2023-06-03T08:51:21.5366667+00:00 An alternative to using global counters to manage threads --
Assuming that the process using the MFC worker threads is a GUI process.
In the main process get the work started
AfxBeginThread(ThreadController, m_hWnd);
Worker details-
// Post this to the GUI thread when finished #define WM_THREADSFINISHED WM_APP + 42 void A() { TRACE(_T("In %s, tid %d\n"), _T(__FUNCTION__), GetCurrentThreadId()); } UINT B(LPVOID pv) { auto tid = GetCurrentThreadId(); TRACE(_T("In %s, tid %d\n"), _T(__FUNCTION__), tid); Sleep(RandomWait()); // Simulate doing work TRACE(_T("%s on tid %d Finished\n"), _T(__FUNCTION__), tid); return 0; } void C() { TRACE(_T("In %s, tid %d\n"), _T(__FUNCTION__), GetCurrentThreadId()); } UINT ThreadController(LPVOID pv) { HWND hDlg = reinterpret_cast<HWND>(pv); const int THREADS{ 8 }; HANDLE hBworkers[THREADS]{}; TRACE(_T("In %s, thread %d\n"), _T(__FUNCTION__), GetCurrentThreadId()); A(); // Create threads suspended to relieably retrieve handles for (int i = 0; i < THREADS; i++) { auto pcwt = AfxBeginThread(B, NULL, 0, 0, CREATE_SUSPENDED); ASSERT_VALID(pcwt); ::DuplicateHandle(GetCurrentProcess(), pcwt->m_hThread, GetCurrentProcess(), &hBworkers[i], 0, FALSE, DUPLICATE_SAME_ACCESS); ASSERT(hBworkers[i] != NULL); pcwt->ResumeThread(); } auto result = ::WaitForMultipleObjects(THREADS, hBworkers, TRUE, INFINITE); ASSERT(result >= WAIT_OBJECT_0 && result <= (WAIT_OBJECT_0 + THREADS - 1)); // All threads are finished // Optional loop through HANDLE array and check thread return codes with GetExitCodeThread C(); // Close thread handles for (int i = 0; i < THREADS; i++) CloseHandle(hBworkers[i]); // Let UI thread know all work is done ::PostMessage(hDlg, WM_THREADSFINISHED, 0, 0); return 0; }