Hello I want to sync two threads one incrementing a variable and other decrementing it.
The result that I want looks like:
Thread #0 j = 1
Thread #1 j = 0
Thread #0 j = 1
Thread #1 j = 0
And so on.. but my code sometimes works like that in some cases it print really weird values. I supose that I have some undefined behavior in somewhere but I can't figured out what is really happen.
My code consist in a HANDLE ghMutex that containg the handler of my mutex:
My main function:
int main(void)
{
HANDLE aThread[THREADCOUNT];
ghMutex = CreateMutex(NULL, FALSE, NULL);
aThread[0] = (HANDLE)_beginthreadex(NULL, 0, &inc, NULL, CREATE_SUSPENDED, 0);
aThread[1] = (HANDLE)_beginthreadex(NULL, 0, &dec, NULL, CREATE_SUSPENDED, 0);
ResumeThread(aThread[0]);
ResumeThread(aThread[1]);
WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
printf("j = %d\n", j);
for (int i = 0; i < THREADCOUNT; i++)
CloseHandle(aThread[i]);
CloseHandle(ghMutex);
return 0;
}
Inc function:
unsigned int __stdcall inc(LPVOID)
{
for (volatile int i = 0; i < MAX; ++i)
{
WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
j++;
printf("Thread %d j = %d\n", GetCurrentThreadId(), j);
ReleaseMutex(ghMutex);
}
_endthread();
return TRUE;
}
Dec function:
unsigned int __stdcall dec(void*)
{
for (volatile int i = 0; i < MAX; ++i)
{
WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
j--;
printf("Thread %d j = %d\n", GetCurrentThreadId(), j);
ReleaseMutex(ghMutex);
}
_endthread();
return TRUE;
}
I need a win api solution in std c++98.
A mutex is not the right tool to synchronize two threads, it is there to protect a resource. You do have a resource j which is protected by your mutex, however the sequence of which thread gets the lock is undefined, so you can have the case where dec gets called several times before inc has the chance to run.
If you want to synchronize the order of the threads you will have to use another synchronization primitive, for example a semaphore. You could, for example, increment the semaphore in inc and decrement it in dec. This would be the classic producer - consumer relationship where the producer will be stalled when the semaphore reaches its maximum value and the consumer will wait for items to consume.
Sorry, no WinAPI C++98 solution from me because that would be silly, but I hope I pointed you to the right direction.
windows mutex object guarantees exclusive ownership, but does not care about the ownership order. so that the same thread can capture several times in a row while others will wait.
for your task you need signal to another thread, when your task is done, and then wait for signal from another thread. for this task can be used event pair for example. thread (i) signal event (1-i) and wait on event (i). for optimize instead 2 calls -
SetEvent(e[1-i]); WaitForSingleObject(e[i], INFINITE);
we can use single call SignalObjectAndWait
SignalObjectAndWait(e[1-i], e[i], INFINITE, FALSE)
of course start and end of loop require special care. for inc
HANDLE hObjectToSignal = _hEvent[1], hObjectToWaitOn = _hEvent[0];
for (;;)
{
_shared_value++;
if (!--n)
{
SetEvent(hObjectToSignal);
break;
}
SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, INFINITE, FALSE);
}
and for dec
HANDLE hObjectToSignal = _hEvent[0], hObjectToWaitOn = _hEvent[1];
WaitForSingleObject(hObjectToWaitOn, INFINITE);
for (;;)
{
--_shared_value;
if (!--n)
{
break;
}
SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, INFINITE, FALSE);
}
if write full test, with error checking
struct Task
{
HANDLE _hEvent[4];
ULONG _n;
LONG _iTasks;
LONG _shared_value;
Task()
{
RtlZeroMemory(this, sizeof(*this));
}
~Task()
{
ULONG n = RTL_NUMBER_OF(_hEvent);
do
{
if (HANDLE hEvent = _hEvent[--n]) CloseHandle(hEvent);
} while (n);
}
ULONG WaitTaskEnd()
{
return WaitForSingleObject(_hEvent[2], INFINITE);
}
ULONG WaitTaskReady()
{
return WaitForSingleObject(_hEvent[3], INFINITE);
}
void SetTaskReady()
{
SetEvent(_hEvent[3]);
}
void End()
{
if (!InterlockedDecrement(&_iTasks)) SetEvent(_hEvent[2]);
}
void Begin()
{
InterlockedIncrementNoFence(&_iTasks);
}
static ULONG WINAPI IncThread(PVOID p)
{
return reinterpret_cast<Task*>(p)->Inc(), 0;
}
void Inc()
{
if (WaitTaskReady() == WAIT_OBJECT_0)
{
if (ULONG n = _n)
{
HANDLE hObjectToSignal = _hEvent[1], hObjectToWaitOn = _hEvent[0];
for (;;)
{
if (_shared_value) __debugbreak();
if (n < 17) DbgPrint("Inc(%u)\n", n);
_shared_value++;
if (!--n)
{
SetEvent(hObjectToSignal);
break;
}
if (SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, INFINITE, FALSE) != WAIT_OBJECT_0)
{
break;
}
}
}
}
End();
}
static ULONG WINAPI DecThread(PVOID p)
{
return reinterpret_cast<Task*>(p)->Dec(), 0;
}
void Dec()
{
if (WaitTaskReady() == WAIT_OBJECT_0)
{
if (ULONG n = _n)
{
HANDLE hObjectToSignal = _hEvent[0], hObjectToWaitOn = _hEvent[1];
if (WaitForSingleObject(hObjectToWaitOn, INFINITE) == WAIT_OBJECT_0)
{
for (;;)
{
--_shared_value;
if (_shared_value) __debugbreak();
if (n < 17) DbgPrint("Dec(%u)\n", n);
if (!--n)
{
break;
}
if (SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, INFINITE, FALSE) != WAIT_OBJECT_0)
{
break;
}
}
}
}
}
End();
}
ULONG Create()
{
ULONG n = RTL_NUMBER_OF(_hEvent);
do
{
if (HANDLE hEvent = CreateEventW(0, n > 2, 0, 0)) _hEvent[--n] = hEvent;
else return GetLastError();
} while (n);
return NOERROR;
}
ULONG Start()
{
static PTHREAD_START_ROUTINE aa[] = { IncThread, DecThread };
ULONG n = RTL_NUMBER_OF(aa);
do
{
Begin();
if (HANDLE hThread = CreateThread(0, 0, aa[--n], this, 0, 0))
{
CloseHandle(hThread);
}
else
{
n = GetLastError();
End();
return n;
}
} while (n);
return NOERROR;
}
ULONG Start(ULONG n)
{
_iTasks = 1;
ULONG dwError = Start();
_n = dwError ? 0 : n;
SetTaskReady();
End();
return dwError;
}
};
void TaskTest(ULONG n)
{
Task task;
if (task.Create() == NOERROR)
{
task.Start(n);
task.WaitTaskEnd();
}
}
note, that no any sense declare local variable (which will be accessed only from single thread and not accessed by any interrupts, etc) as volatile
also when we write code, like:
// thread #1
write_shared_data();
SetEvent(hEvent);
// thread #2
WaitForSingleObject(hEvent, INFINITE);
read_shared_data();
inside SetEvent(hEvent); was atomic write to event state with release semantic (really stronger of course) and inside wait for event function - atomic read it state with more than acquire semantic. as result all what thread #1 write to memory before SetEvent - will be visible to thread #2 after Wait for event (if wait finished as result of call Set from thread #1)
Related
(Note before starting: Although my question is general, my code needs to compile with legacy Visual Studio 2008 MFC application and has to use MFC or win32 synchronization, please avoid answers using ie boost or c++ 11)
I am trying to implement a Thread Safe Pipe (A Queue with a single reader and a single writer), I did the following:
template<class T>
class CMultiThreadPipe {
private:
HANDLE hSemaphore, hTerminateEvent1, hTerminateEvent2;
CRITICAL_SECTION listMutex;
CList<T*, T*> list;
public:
CMultiThreadPipe() {
InitializeCriticalSection(&listMutex);
hSemaphore = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
hTerminateEvent1 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
hTerminateEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}
// pdata must be allocated with new. The dequeueing thread will delete it
void Enqueue(T* pdata) {
EnterCriticalSection(&listMutex);
list.AddHead(pdata);
LeaveCriticalSection(&listMutex);
ReleaseSemaphore(hSemaphore, 1, NULL);
}
// if Dequeue returns null it means the pipe was destroyed and no further queue method calls are legal
// Dequeue caller is responsible to delete the returned instance
T* Dequeue()
{
HANDLE handles[] = { hTerminateEvent1, hSemaphore };
DWORD waitRes = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
if (waitRes==WAIT_OBJECT_0) {
SetEvent(hTerminateEvent2);
return NULL; // terminated
}
EnterCriticalSection(&listMutex);
T* elem = list.RemoveTail();
LeaveCriticalSection(&listMutex);
return elem; // handler must delete item
}
void Destroy() {
SetEvent(hTerminateEvent1);
WaitForSingleObject(hTerminateEvent2, INFINITE);
EnterCriticalSection(&listMutex);
POSITION pos = list.GetHeadPosition();
for (int i = 0; i < list.GetCount(); i++) delete list.GetNext(pos);
LeaveCriticalSection(&listMutex);
DeleteCriticalSection(&listMutex);
CloseHandle(hSemaphore);
}
~CMultiThreadPipe() {
Destroy();
}
};
The code is used like this:
class QueueData {
public:
QueueData(int i) : m_data(i) {};
int m_data;
};
UINT DequeueThreadProc(LPVOID dummy);
CMultiThreadedPipe<QueueData>* pPipe = NULL;
void main() {
pPipe = new CMultiThreadedPipe<QueueData>();
start new thread running DequeueThreadProc
int counter=0;
for (int counter=0; counter<10; counter++)
{
pPipe->Enqueue(new QueueData(counter));
Sleep(300);
}
delete pPipe;
}
UINT DequeueThreadProc(LPVOID ignore)
{
QueueData* queueData;
while ((queueData = pPipe->Dequeue()) != NULL) {
delete queueData;
Sleep(1000);
};
return 0;
}
The issue I have is with termination, in the above implementation, when the pipe is destroyed (always by the enqueing thread) it is waiting for the dequeing thread to know that it terminated before deleting the queue. It has to do that to prevent a situation where the dequeing thread tries to dequeue after the pipe is destroyed.
If the dequeing thread does not keep calling dequeue the first thread will hang in the destructor, also if the dequeing thread waits a long time between calls to dequeue the destructor of the first thread will get stuck there accordingly.
I read various posts about it none mentions safe destruction. Any help appreciated !
for safe destruction object, which accessed from multiple threads you need use reference counting on it. before pass object pointer to new thread - you increment reference on object. when thread no more using object, or if create thread fail, you decrement reference count. when last reference on object released - you can safe call destructor for object. and you not need here wait for any threads.
also for implement such queue - in windows exist special object - named I/O Completion Ports in user space (in kernel space in know as KQUEUE). with this object - implementation will be more efficient and simply - you not need manage self list (CList in your code), synchronize access to it - all this will be done in kernel space for you (PostQueuedCompletionStatus -> KeInsertQueue, GetQueuedCompletionStatus -> KeRemoveQueue). you need create only iocp, (kqueue) object.
class CMultiThreadPipe {
public:
class __declspec(novtable) QueueData {
public:
virtual void ProcessItem() = 0;
virtual ~QueueData()
{
DbgPrint("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, this);
}
QueueData()
{
DbgPrint("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, this);
}
};
private:
HANDLE _hIOCP;
LONG _dwRef;
ULONG _nThreads;
void DequeueThreadProc()
{
ULONG NumberOfBytesTransferred;
QueueData* pData;
OVERLAPPED* pOverlapped;
while (GetQueuedCompletionStatus(_hIOCP,
&NumberOfBytesTransferred,
(ULONG_PTR*)&pData,
&pOverlapped, INFINITE))
{
if (pData)
{
pData->ProcessItem();
}
else
{
break;
}
}
Release();
}
__declspec(noreturn) static DWORD CALLBACK _DequeueThreadProc(PVOID pThis)
{
reinterpret_cast<CMultiThreadPipe*>(pThis)->DequeueThreadProc();
FreeLibraryAndExitThread((HMODULE)&__ImageBase, 0);
}
~CMultiThreadPipe()
{
if (_hIOCP)
{
CloseHandle(_hIOCP);
}
}
public:
CMultiThreadPipe() : _dwRef(1), _hIOCP(0)
{
}
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef))
{
delete this;
}
}
ULONG Create(DWORD NumberOfDequeueThreads)
{
if (_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, NumberOfDequeueThreads))
{
ULONG n = 0;
do
{
HMODULE hModule;
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (PCWSTR)_DequeueThreadProc, &hModule))
{
AddRef();
if (HANDLE hThread = CreateThread(0, 0, _DequeueThreadProc, this, 0, 0))
{
CloseHandle(hThread);
n++;
}
else
{
Release();
FreeLibrary(hModule);
}
}
} while (--NumberOfDequeueThreads);
_nThreads = n;
return n ? NOERROR : ERROR_GEN_FAILURE;
}
return GetLastError();
}
ULONG Enqueue(QueueData* pData)
{
return PostQueuedCompletionStatus(_hIOCP, 0, (ULONG_PTR)pData, 0) ? NOERROR : GetLastError();
}
void Destroy()
{
if (ULONG n = _nThreads)
{
do
{
PostQueuedCompletionStatus(_hIOCP, 0, 0, 0);
} while (--n);
}
}
};
and usage:
class QueueData : public CMultiThreadPipe::QueueData
{
int m_data;
virtual void ProcessItem()
{
DbgPrint("%x: %s<%p>(%u)\n", GetCurrentThreadId(), __FUNCTION__, this, m_data);
delete this;
}
public:
QueueData(int i) : m_data(i) {};
};
void testQueue()
{
if (CMultiThreadPipe* pPipe = new CMultiThreadPipe)
{
if (pPipe->Create(8) == NOERROR)
{
int n = 64;
do
{
if (QueueData* pData = new QueueData(n))
{
if (pPipe->Enqueue(pData))
{
delete pData;
}
}
} while (--n);
pPipe->Destroy();
}
pPipe->Release();
}
}
note with such CMultiThreadPipe implementations - you not need wait when working threads exit. even if your code inside dll and you unload dll - you not need wait. every thread have own reference for object and module. and release it on exit
I have 2 processes:
The first one creates a memory mapped region, a mutex and spawns
the second process. Then writes some pairs of numbers in the memory mapped region.
The second one opens the memory mapped region, opens the mutex and then reads the numbers written by the process 1.
I intended the first process to write a pair of numbers and the second one to immediately read it.
The process 2 seems to be starving.
What did I do wrong?
Process 1:
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
DWORD memSize = 400 * sizeof(DWORD);
HANDLE map_file = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, memSize, TEXT("mem1"));
if (map_file == NULL)
{
_tprintf(_T("(Parent) File mapping is null\n"));
return 1;
}
char* map_ptr = (char *) MapViewOfFile(map_file, FILE_MAP_READ, 0, 0, 0);
if (map_ptr == NULL)
{
_tprintf(_T("(Parent) PTR is null \n"));
}
HANDLE hMutex = CreateMutex(NULL, TRUE, _T("mt"));
LPTSTR szCmdline = _tcsdup(TEXT("C:\\Users\\cristi\\source\\repos\\process_synchronization_reader\\Debug\\process_synchronization_reader.exe"));
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(NULL, szCmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
_tprintf(_T("Process created\n"));
}
_tprintf(_T("pare ca s-a creat"));
for (int i = 1; i <= 200; ++i)
{
WaitForSingleObject(hMutex, INFINITE);
_tprintf(_T("(Parent %d) writing from the parent\n"), i);
DWORD a, b;
CopyMemory((LPVOID) &a, map_ptr, sizeof(DWORD));
map_ptr += sizeof (DWORD);
CopyMemory((LPVOID) &b, map_ptr, sizeof(DWORD));
map_ptr += sizeof(DWORD);
ReleaseMutex(hMutex);
}
int n;
cin >> n;
CloseHandle(map_file);
return 0;
}
Process 2:
#include "stdafx.h"
#include <windows.h>
int main()
{
HANDLE map_file = OpenFileMapping(FILE_MAP_READ, FALSE, TEXT("mem1"));
if (map_file == NULL)
{
_tprintf(_T("(Child) File mapping is null\n"));
return 1;
}
char* map_ptr = (char *) MapViewOfFile(map_file, FILE_MAP_READ, 0, 0, 0);
if (map_ptr == NULL)
{
_tprintf(_T("(Child) PTR is null \n"));
}
_tprintf(_T("(CHILD) BEfore reading the first number\n"));
HANDLE hMutex = OpenMutex(SYNCHRONIZE, TRUE, _T("mt"));
for (int i = 1; i <= 200; i++)
{
WaitForSingleObject(hMutex, INFINITE);
DWORD a = i;
DWORD b = 2 * i;
CopyMemory((LPVOID) map_ptr, &a, sizeof(DWORD));
map_ptr += sizeof(DWORD);
CopyMemory((LPVOID) map_ptr, &b, sizeof(DWORD));
map_ptr += sizeof(DWORD);
_tprintf(_T("[================================================]\n"));
_tprintf(_T("( %d %d )\n"), a, b);
_tprintf(_T("[=================================================]\n"));
ReleaseMutex(hMutex);
}
return 0;
}
for got sequential write/read from shared memory we need 2 events (let name it Low and High).
first thread:
write data
signal Low event
wait on High event or break loop
goto 1
second thread:
wait on Low event
read data
break loop or signal High event
goto 1
unlike this solution mutex can not provide a sequence of reading / writing. mutex guarantee that until one thread will be access shared data (read or write) another thread will be not do this in concurrent. but this can not prevent several times in a row for write or read. really - insert messagebox in begin of process 2 - before he first time try acquire mutex - first process already many time acquire and release mutex. or if one thread will be suspended between release and wait for mutex - meanwhile another thread many time wait and release it. so code can look like:
struct SHARED_DATA
{
ULONG id;
ULONG nLoops;
BOOL bTask;
};
DWORD proc2(SHARED_DATA* p)
{
if (HANDLE hLowEvent = OpenEvent(SYNCHRONIZE, FALSE, L"LowEvent"))
{
if (HANDLE hHighEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, L"hHighEvent"))
{
ULONG id = GetCurrentThreadId();
for(;;)
{
if (WaitForSingleObject(hLowEvent, INFINITE) != WAIT_OBJECT_0)
{
break;
}
// ++ checking for sequence
if (p->id == id)
{
__debugbreak();// must never be
}
p->id = id;
// -- checking for sequence
if (!p->bTask)
{
// no more task
break;
}
// task done
p->bTask = FALSE;
// signal to #1
if (!SetEvent(hHighEvent))
{
break;
}
}
CloseHandle(hHighEvent);
}
CloseHandle(hLowEvent);
}
return 0;
}
DWORD proc1(SHARED_DATA* p)
{
if (HANDLE hLowEvent = CreateEvent(0, FALSE, FALSE, L"LowEvent"))
{
if (HANDLE hHighEvent = CreateEvent(0, FALSE, FALSE, L"hHighEvent"))
{
ULONG id = GetCurrentThreadId();
p->nLoops = 0x1000;
p->id = 0;
p->bTask = FALSE;
// exec proc2 here
goto __1;
do
{
if (WaitForSingleObject(hHighEvent, INFINITE) != WAIT_OBJECT_0)
{
break;
}
if (p->bTask)
{
__debugbreak();
}
// ++ checking for sequence
if (p->id == id)
{
__debugbreak();// must never be
}
__1:
p->id = id;
// -- checking for sequence
p->bTask = 0 < --p->nLoops;
// signal to #2
if (!SetEvent(hLowEvent))
{
break;
}
} while (p->nLoops);
CloseHandle(hHighEvent);
}
CloseHandle(hLowEvent);
}
return 0;
}
You create a mutex as initially owned (the second argument to CreateMutex is TRUE) and then you call a wait-function on it. So even after calling ReleaseMutex, it is still owned by a main thread of the first process.
Either change the argument to FALSE, or skip calling WaitForSingleObject for the first loop iteration.
I have written a simple thread pool in c++ using posix, on Ubuntu, which compiles and runs as expected, when started from within Code::Blocks. However, when I execute the program from the terminal, it hangs after the first thread task completes.
Here is the code in the execute() function of the task object:
void TestTask::execute()
{
FILE* file;
std::string fname = "output";
char n = (char)m_id;
fname+=n;
fname+=".txt";
file = fopen(fname.c_str(), "w");
for (int i = 0; i < 10; i++)
{
fprintf(file,"Thread ID: %d, count : %d\n", m_id, i);
}
fclose(file);
}
As you can see, the code simply outputs some data to a seperate file for each thread.
I have also tried:
void TestTask::execute()
{
for (int i = 0; i < 10; i++)
{
printf("Thread ID: %d, count : %d\n", m_id, i);
}
}
As previously stated, both of these versions work perfectly when the project is built and run in code::blocks, but when the executable is run from a command line, it hangs.
Thread pool code:
ThreadPool::ThreadPool(int iNumThreads):mRunning(false), mShuttingDown(false)
{
mThreads.resize(iNumThreads);
if(iNumThreads > 0)
for (int i = 0; i < iNumThreads; i++)
{
pthread_create(&mThreads[i], NULL, ThreadPool::ThreadFunc,(void*) this);
}
pthread_mutex_init(&mMemLock, NULL); //mutex' are initialised unlocked.
}
void ThreadPool::stop()
{
pthread_mutex_lock(&mMemLock);
mRunning = false;
pthread_mutex_unlock(&mMemLock);
}
void ThreadPool::start()
{
//signal all threads to wake up and start extracting tasks to execute.
if(!mTasks.empty())
pthread_cond_broadcast(&mConditionSignal);
mRunning = true;
}
bool ThreadPool::shutDown()
{
//terminate all threads, regardless of status. Brute force approach, only to be used in race conditions or major errors.
for (size_t i = 0; i < mThreads.size();i++)
{
int err = pthread_cancel(mThreads[i]);
if(err != 0)
return false;
}
return true;
}
bool ThreadPool::shutDownClean()
{
//set flag and wait for all threads to terminate normally
mShuttingDown = true;
for(size_t i = 0; i < mThreads.size();i++)
{
pthread_join(mThreads[i], NULL);
}
return true;
}
bool ThreadPool::addTask(Task* pTask, bool start)
{
mTasks.push_back(pTask);
if(start)
{
mRunning = true;
pthread_cond_signal(&mConditionSignal);
}
return true;
//wake up a single thread to deal with a single task (first come, first served)
}
void ThreadPool::addCompleteTask(Task* pTask)
{
mCompletedTasks.push_back(pTask);
}
Task* ThreadPool::getNextCompletedTask()
{
Task* t = mCompletedTasks.front();
mCompletedTasks.pop_front();
return t;
}
Task* ThreadPool::getNextScheduledTask()
{
Task* t = mTasks.front();
mTasks.pop_front();
return t;
}
bool ThreadPool::isRunning()
{
return (mRunning);
}
ThreadPool::~ThreadPool()
{
if(!mShuttingDown)
shutDownClean();
mThreads.clear();
mTasks.clear();
mCompletedTasks.clear();
}
void* ThreadPool::ThreadFunc(void* ptr)
{
ThreadPool *pool = (ThreadPool*)ptr;
Task *task;
//infinite do nothing loop. The thread will spin it's wheels while it waits for work.
while(true)
{
pthread_mutex_lock(&pool->getMutex());
while(pool->isRunning() && !pool->hasCurrentTasks())
{
//wait on signal from pool to begin
pthread_cond_wait(&pool->getCondition(), &pool->getMutex());
}
//If the pool has tasks needing executed
if(pool->hasCurrentTasks())
{
//get the task and execute it, after unlocking the mutex
task = pool->getNextScheduledTask();
pthread_mutex_unlock(&pool->getMutex());
task->execute();
//if the task has return data, then add it to the list of completed tasks, for the main thread to evaluate
if(task->hasReturnData())
{
pthread_mutex_lock(&pool->getMutex());
pool->addCompleteTask(task);
pthread_mutex_unlock(&pool->getMutex());
}
//if not, we need not keep the task object any more
else
delete task;
}
else
{
//stop the pool running, and unlock the mutex
pool->stop();
pthread_mutex_unlock(&pool->getMutex());
}
if(pool->isShuttingDown())
{
break;
}
}
pthread_exit(NULL);
}
Here is the code from main.cpp:
int main(int argc, char** argv)
{
int numCores = sysconf(_SC_NPROCESSORS_ONLN);
ThreadPool* pool = new ThreadPool(numCores);
for (int i = 0; i < numCores; i++)
{
if(!pool->addTask(new TestTask(i)))
pool->shutDown();
}
pool->start();
if(!pool->shutDownClean())
return 1;
else
return 0;
}
I modified the "Using Condition Variables" example in MSDN. I created several threads, which have their own producer/consumer separately.
But the program will often hang when attached to debugger. It always hangs at SleepConditionVariableCS. When I break all and continue in debugger, the program will continue to run. I found that WakeAllConditionVariable didn't wake up some thread calls SleepConditionVariableCS, for the Ptr in PExecutingTask->BufferNotFull or PExecutingTask->BufferNotEmpty was already 0x00000000(I thought that means the condition variable is wakened).
When not attached to debugger, the program will not hang.
Has anyone encountered this problem before? How to solve it?
Here is the code:
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFFER_SIZE 100
DWORD WINAPI ProducerThreadProc (PVOID p);
DWORD WINAPI ConsumerThreadProc (PVOID p);
class ExecutingTask
{
public:
void Initialize()
{
InitializeConditionVariable (&BufferNotEmpty);
InitializeConditionVariable (&BufferNotFull);
InitializeCriticalSection (&BufferLock);
QueueSize = 0;
StopRequested = FALSE;
hProducer = CreateThread(NULL, 0, ProducerThreadProc, (PVOID)this, 0, NULL);
for (int i = 0; i < 20; i++)
{
PhConsumers[i] = CreateThread (NULL, 0, ConsumerThreadProc, (PVOID)this, 0, NULL);
}
}
void Stop()
{
EnterCriticalSection (&BufferLock);
StopRequested = TRUE;
LeaveCriticalSection (&BufferLock);
WakeAllConditionVariable (&BufferNotFull);
WakeAllConditionVariable (&BufferNotEmpty);
WaitForSingleObject(hProducer, INFINITE);
WaitForMultipleObjects(20, (HANDLE *)&PhConsumers, true, INFINITE);
CloseHandle(hProducer);
for (unsigned int i = 0; i < 20; i++)
{
CloseHandle(PhConsumers[i]);
}
}
public:
ULONG QueueSize;
CONDITION_VARIABLE BufferNotEmpty;
CONDITION_VARIABLE BufferNotFull;
CRITICAL_SECTION BufferLock;
BOOL StopRequested;
HANDLE PhConsumers[20];
HANDLE hProducer;
};
DWORD WINAPI ProducerThreadProc (PVOID p)
{
ExecutingTask* PExecutingTask = (ExecutingTask*)p;
while (true)
{
EnterCriticalSection (&PExecutingTask->BufferLock);
while (PExecutingTask->QueueSize >= BUFFER_SIZE && PExecutingTask->StopRequested == FALSE)
{
// Buffer is full - sleep so consumers can get items.
SleepConditionVariableCS (&PExecutingTask->BufferNotFull, &PExecutingTask->BufferLock, INFINITE);
}
if (PExecutingTask->StopRequested == TRUE)
{
LeaveCriticalSection (&PExecutingTask->BufferLock);
break;
}
// Produce an item.
PExecutingTask->QueueSize++;
LeaveCriticalSection (&PExecutingTask->BufferLock);
WakeConditionVariable (&PExecutingTask->BufferNotEmpty);
}
return 0;
}
DWORD WINAPI ConsumerThreadProc (PVOID p)
{
ExecutingTask* PExecutingTask = (ExecutingTask*)p;
while (true)
{
EnterCriticalSection (&PExecutingTask->BufferLock);
while (PExecutingTask->QueueSize == 0 && PExecutingTask->StopRequested == FALSE)
{
// Buffer is empty - sleep so producers can create items.
SleepConditionVariableCS (&PExecutingTask->BufferNotEmpty, &PExecutingTask->BufferLock, INFINITE);
}
if (PExecutingTask->StopRequested == TRUE)
{
LeaveCriticalSection (&PExecutingTask->BufferLock);
break;
}
// Consume an item.
PExecutingTask->QueueSize--;
LeaveCriticalSection (&PExecutingTask->BufferLock);
WakeConditionVariable (&PExecutingTask->BufferNotFull);
}
return 0;
}
DWORD WINAPI ThreadProc (PVOID p)
{
ExecutingTask task;
task.Initialize();
Sleep(1000);
task.Stop();
printf ("%u exit\n", &task);
return 0;
};
int main ( void )
{
HANDLE hTaskThreads[50];
for (int i = 0; i < 50; i++)
{
hTaskThreads[i] = CreateThread (NULL, 0, ThreadProc, NULL, 0, NULL);
}
WaitForMultipleObjects(50, hTaskThreads, true, INFINITE);
for (int i = 0; i < 50; i++)
{
CloseHandle(hTaskThreads[i]);
}
}
I'm trying to get my Main Program to start a thread that manages a section of my program... The thread that gets made creates a sub thread for each element of an array. The sub thread sets the array element to not available, delays by 10 seconds, and then sets the element to available. The main thread, however, continues normal execution until the array element is available.
However, it doesn't seem to apply a delay...
Please note the array is a global array.
What am I doing wrong?
The below code will outline what I want better then the explanation...
DWORD WINAPI SubThread(void* lpParam)
{
int i = (int)lpParam;
printf("Sub Thread Ran");
structureArray[i].available= false;
Sleep(10000);
structureArray[i].available = true;
return 0;
}
DWORD WINAPI MainThread(void * lpParam)
{
while(true)
{
for(int i = 0; i < structureArray.size(); i++)
{
if(structureArray[i].available)
{
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SubThread, (LPVOID)i, 0, NULL);
}
}
}
}
Your global variable is not thread safe, it seems.
Try using a thread safe collection or lock the access yourself (solution is windows specific, inferred from your WINAPI specifier):
class CriticalSection
{
public:
CriticalSection()
{ ::InitializeCriticalSection(&m_cs); }
~CriticalSection()
{ ::DeleteCriticalSection(&m_cs); }
void Enter()
{ ::EnterCriticalSection(&m_cs); }
void Leave()
{ ::LeaveCriticalSection(&m_cs); }
private:
// private to prevent copying
CriticalSection(const CriticalSection&);
CriticalSection& operator=(const CriticalSection&);
CRITICAL_SECTION m_cs;
};
class Lock
{
public:
Lock(CriticalSection& a_section): m_section(a_section)
{ m_section.Enter(); }
~Lock()
{ m_section.Leave(); }
private:
Lock(const CSLock&);
Lock& operator=(const CSLock&);
CriticalSection& m_section;
};
CriticalSection g_dontUseGlobalCS;
DWORD WINAPI SubThread(void* lpParam)
{
int i = (int)lpParam;
printf("Sub Thread runs for...");
Sleep(10000);
{ // use scope to destroy lock...
Lock(g_dontUseGlobalCS);
structureArray[i].available= true;
} // exactly here
// note: a new thread could already be running here
// for debugging purposes add this:
printf("10 seconds.");
return 0;
}
DWORD WINAPI MainThread(void * lpParam)
{
while(true)
{
for(int i = 0; i < structureArray.size(); i++)
{
Lock(g_dontUseGlobalCS);
if(structureArray[i].available)
{
// set the thread availability here
structureArray[i].available = false;
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SubThread, (LPVOID)i, 0, NULL);
}
}
}
}
I also moved the set of the availability flag to the creator thread. Usually you would use other mechanisms to realize what you are trying to do:
Look into windows synchronization functions or wiki