[英]Sync object to wait without blocking UI in C++ (C++ Builder)
I have a task on multi-threading in C++ in which there is a critical code block. 我有一个C ++中的多线程任务,其中有一个关键代码块。 Main requirements are the followings: 主要要求如下:
So I created the following class in C++ Builder to fulfill the requirements. 因此,我在C ++ Builder中创建了以下类来满足要求。 Do you think there is any issue in it? 您是否认为其中存在任何问题?
Many thanks for your time in advance! 非常感谢您提前抽出宝贵的时间!
Class declaration / definition: 类声明/定义:
#include "windows.h"
#include <vector>
#include <algorithm>
class TSyncObject
{
private:
DWORD WorkingThreadId;
std::vector<DWORD> WaitingThreadIds;
TCriticalSection *Section;
HANDLE Event;
//---------------------------------------------------------------------------
bool CanThreadWait(const DWORD ThreadId)
{
bool CanWait;
Section->Enter();
try
{
bool AlreadyWaiting =
std::find(WaitingThreadIds.begin(), WaitingThreadIds.end(), ThreadId) != WaitingThreadIds.end();
CanWait = !AlreadyWaiting && ThreadId != WorkingThreadId && WorkingThreadId;
if (CanWait)
{
WaitingThreadIds.push_back(ThreadId);
}
}
__finally
{
Section->Leave();
}
return CanWait;
}
//---------------------------------------------------------------------------
void Acquire(const DWORD ThreadId)
{
Section->Enter();
try
{
WorkingThreadId = ThreadId;
std::vector<DWORD>::iterator Pos =
std::find(WaitingThreadIds.begin(), WaitingThreadIds.end(), ThreadId);
if (Pos != WaitingThreadIds.end())
{
WaitingThreadIds.erase(Pos);
}
}
__finally
{
Section->Leave();
}
}
//---------------------------------------------------------------------------
void HandleError()
{
Section->Enter();
try
{
if (GetCurrentThreadId() == WorkingThreadId ||
(WaitingThreadIds.empty() && !WorkingThreadId))
{
WorkingThreadId = 0;
SetEvent(Event);
}
}
__finally
{
Section->Leave();
}
}
//---------------------------------------------------------------------------
public:
//---------------------------------------------------------------------------
enum TThreadAcquire {Acquired, WaitEjected, AppTerminated, Failed};
//---------------------------------------------------------------------------
TSyncObject() :
WorkingThreadId(0),
Section(new TCriticalSection()),
Event(CreateEventW(0, 0, 1, 0))
{
}
//---------------------------------------------------------------------------
virtual ~TSyncObject()
{
CloseHandle(Event);
delete Section;
}
//---------------------------------------------------------------------------
TThreadAcquire Acquire()
{
try
{
DWORD CurrentThreadId = GetCurrentThreadId();
if (WaitForSingleObject(Event, 0) != WAIT_OBJECT_0)
{
if (!CanThreadWait(CurrentThreadId))
{
return WaitEjected;
}
while (!Application->Terminated &&
(MsgWaitForMultipleObjects(1, &Event, 0, INFINITE, QS_ALLINPUT) - WAIT_OBJECT_0))
{
Application->ProcessMessages();
}
if (Application->Terminated)
{
return AppTerminated;
}
}
Acquire(CurrentThreadId);
return Acquired;
}
catch (...)
{
HandleError();
return Failed;
}
}
//---------------------------------------------------------------------------
void Release()
{
Section->Enter();
try
{
WorkingThreadId = 0;
SetEvent(Event);
}
__finally
{
Section->Leave();
}
}
//---------------------------------------------------------------------------
};
I will use it like this: 我将这样使用它:
TSyncObject Sync; // Global for all threads
//...
A thread uses the object like this: 线程使用这样的对象:
TSyncObject::TThreadAcquire Acq = Sync.Acquire();
try
{
if (Acq == TSyncObject::WaitEjected)
{
//...
return;
}
else if (Acq == TSyncObject::AppTerminated)
{
//...
return;
}
else if (Acq == TSyncObject::Failed)
{
//...
return;
}
// critical code block
}
__finally
{
Sync.Release();
}
I would suggest replacing all of the event and criticalsection handling with a simple semaphore instead. 我建议用一个简单的信号量代替所有的事件和临界区处理。
Try something more like this: 尝试更多类似这样的方法:
MySyncObject.hpp MySyncObject.hpp
#ifndef MySyncObjectH
#define MySyncObjectH
#include "windows.h"
class TSyncObject
{
private:
HANDLE Semaphore;
public:
enum TThreadAcquire {Acquired, WaitEjected, AppTerminated, Failed};
TSyncObject();
~TSyncObject();
TThreadAcquire Acquire();
void Release();
};
#endif
MySyncObject.cpp MySyncObject.cpp
#include "MySyncObject.hpp"
bool __thread SyncState = 0;
TSyncObject::TSyncObject()
: Semaphore(NULL)
{
Semaphore = CreateSemaphore(NULL, 1, 1, NULL);
if (!Semaphore) RaiseLastOSError();
}
TSyncObject::~TSyncObject()
{
CloseHandle(Semaphore);
}
TSyncObject::TThreadAcquire TSyncObject::Acquire()
{
DWORD dwRet = WaitForSingleObject(Semaphore, 0);
if (dwRet == WAIT_TIMEOUT)
{
if (SyncState != 0)
return WaitEjected;
SyncState = 1;
while (!Application->Terminated)
{
dwRet = MsgWaitForMultipleObjects(1, &Semaphore, FALSE, INFINITE, QS_ALLINPUT);
if ((dwRet == WAIT_OBJECT_0) || (dwRet == WAIT_FAILED))
break;
if (dwRet == (WAIT_OBJECT_0+1))
{
try
{
Application->ProcessMessages();
}
catch (...)
{
//...
}
}
}
}
if (dwRet != WAIT_OBJECT_0)
{
SyncState = 0;
if (Application->Terminated)
return AppTerminated;
return Failed;
}
SyncState = 2;
return Acquired;
}
void TSyncObject::Release()
{
ReleaseSemaphore(Semaphore, 1, NULL);
SyncState = 0;
}
Then use it like this: 然后像这样使用它:
TSyncObject Sync;
...
TSyncObject::TThreadAcquire Acq = Sync.Acquire();
if (Acq == TSyncObject::Acquired)
{
try
{
// critical code block...
}
__finally
{
Sync.Release();
}
}
else
{
if (Acq == TSyncObject::WaitEjected)
{
//...
}
else if (Acq == TSyncObject::AppTerminated)
{
//...
}
else // TSyncObject::Failed
{
//...
}
return;
}
With that said, you might consider writing the class as a singleton instead of declaring a global variable: 话虽如此,您可以考虑将类写为单例而不是声明全局变量:
MySyncObject.hpp MySyncObject.hpp
#ifndef MySyncObjectH
#define MySyncObjectH
#include "windows.h"
class TSyncObject
{
private:
HANDLE Semaphore;
TSyncObject();
public:
enum TThreadAcquire {Acquired, WaitEjected, AppTerminated, Failed};
~TSyncObject();
static TSyncObject& Instance();
TThreadAcquire Acquire();
void Release();
};
#endif
MySyncObject.cpp MySyncObject.cpp
#include "MySyncObject.hpp"
...
static TSyncObject& TSyncObject::Instance()
{
static TSyncObject inst;
return inst;
}
...
TSyncObject::TThreadAcquire Acq = TSyncObject::Instance().Acquire();
if (Acq == TSyncObject::Acquired)
{
try
{
// critical code block...
}
__finally
{
TSyncObject::Instance().Release();
}
}
else
{
if (Acq == TSyncObject::WaitEjected)
{
//...
}
else if (Acq == TSyncObject::AppTerminated)
{
//...
}
else // TSyncObject::Failed
{
//...
}
return;
}
Alternatively: 或者:
MySyncObject.hpp MySyncObject.hpp
#ifndef MySyncObjectH
#define MySyncObjectH
#include "windows.h"
class TSyncObject
{
private:
HANDLE Semaphore;
TSyncObject();
static TSyncObject& Instance();
public:
enum TThreadAcquire {Acquired, WaitEjected, AppTerminated, Failed};
~TSyncObject();
static TThreadAcquire Acquire();
static void Release();
};
#endif
MySyncObject.cpp MySyncObject.cpp
#include "MySyncObject.hpp"
...
TSyncObject::TThreadAcquire TSyncObject::Acquire()
{
TSyncObject &inst = Instance();
// use inst.Semaphore as needed...
}
void TSyncObject::Release()
{
TSyncObject &inst = Instance();
// use inst.Semaphore as needed...
}
TSyncObject::TThreadAcquire Acq = TSyncObject::Acquire();
if (Acq == TSyncObject::Acquired)
{
try
{
// critical code block...
}
__finally
{
TSyncObject::Release();
}
}
else
{
if (Acq == TSyncObject::WaitEjected)
{
//...
}
else if (Acq == TSyncObject::AppTerminated)
{
//...
}
else // TSyncObject::Failed
{
//...
}
return;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.