 |
|
 |
//file: WorkerDlg.h
class CWorkerDlg :
....
CCriticalSection m_Lock;
CWorkerThread m_Thread1;
CWorkerThread m_Thread2;
CWorkerThread m_Thread3;
....
//file: WorkerDlg.cpp
CWorkerDlg::CWorkerDlg(CWnd* pParent /*=NULL*/)
: CDialog(CWorkerDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CWorkerDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_Thread1.AddEvent(m_Event1, EventProc1, this);
m_Thread2.AddEvent(m_Event2, EventProc2, this);
m_Thread3.AddEvent(m_Event3, EventProc3, this);
}
BOOL CWorkerDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_Thread1.Start();
m_Thread2.Start();
m_Thread3.Start();
return TRUE; // return TRUE unless you set the focus to a control
}
void CWorkerDlg::Report(LPCTSTR lpszReport)
{
this->m_Lock.Lock();
{
int nLength = m_EdReport.GetWindowTextLength();
m_EdReport.SetSel(nLength, nLength);
m_EdReport.ReplaceSel(lpszReport);
nLength = m_EdReport.GetWindowTextLength();
m_EdReport.SetSel(nLength, nLength);
}
this->m_Lock.Unlock();
}
void CWorkerDlg::OnBnClickedCleartext()
{
// TODO:
this->m_Lock.Lock();
{
int nLength = m_EdReport.GetWindowTextLength();
m_EdReport.SetSel(0, nLength);
m_EdReport.ReplaceSel(_T(""));
nLength = m_EdReport.GetWindowTextLength();
m_EdReport.SetSel(nLength, nLength);
}
this->m_Lock.Unlock();
}
Taiwan's pauper.
|
|
|
|
 |
|
 |
Excelent work Frank Melber! I've been looking for a simple thread event based system!
=====================
Lars [Large] Werner
lars@werner.no
http://lars.werner.no
Have you tried the ultimate tool for filling your CD/DVDs? http://lars.werner.no/sizeme/
=====================
|
|
|
|
 |
 | Nice  |  | gem.tang | 16:11 26 Aug '05 |
|
 |
It's a very simple and efficent worker thread implementation of "One thread to execute some works synchronously".
Well done Frank.
Hi, i am a comer.
|
|
|
|
 |
|
 |
Thanks for the code, it was close to what i wanted to see. but i need a "worker thread" like this one that would also allow the adding and removal of events even while the thread is running. i have coded one, it seems to work, but i simply can´t assure its totaly safe, so i would like to see somebody else´s implementation. does anybody know of any?
thanks.
|
|
|
|
 |
|
 |
Hello,
I would like to ask whether there is any possibility
a.\to remove queued events or
b.\stop execution of already running threads?
Thanks
|
|
|
|
 |
|
 |
I pressed on Start1 and the thread1 is starting ... while it is waiting i pressed on Stop and then pressed on Start2 or Start1 or Start3 and nothing happened.
Does it mean that we can start a thread and if we stop it we cannot start it over again ?
Yves
|
|
|
|
 |
|
 |
Take a closer look at the event handler of the Stop button. It calls CWorkerThread::Kill(), which terminates all previously added events.
In order to proceed further events in this dialog you have to add them again, or just open a new dialog.
BTW: I have a slow external device on the serial port the state of which must be queried for quite often. This query can take an unpredictable amount of time, even more than the querying frequency itself. Furthermore, commands also must be issued to the device on the same serial port.
Event1: Querying Timer
Event2: Open/Close command (which must have priority to other commands)
Event3: Other device manipulating commands
This class seems to solve my problem perfectly, and as such is an application example.
Many thanks to the author for sharing this excellent solution!
Pepe
|
|
|
|
 |
|
 |
Hi PPM
I too need the same thing.I too want to query my database for evry 3 minuts like that.How can I call the event multiple times in this regard?
Please help me.
Thank you,
Pathi
|
|
|
|
 |
|
 |
To start again I modified code in this way to re-add event after Kill on Stop button:
void CWorkerDlg::OnStop()
{
m_Thread.Kill();
m_Thread.AddEvent(m_Event1, EventProc1, this);
m_Thread.AddEvent(m_Event2, EventProc2, this);
m_Thread.AddEvent(m_Event2, EventProc2, this);
}
but on Start1 button nothing happened.
Can you send an example to handle restart.
Bye.
DoM.
|
|
|
|
 |
|
 |
I haven't looked at the code yet, but one thing was immediately obvious to me: this class does NOT allow an unlimited number of functions to be fired, since WaitForMultipleObjects cannot wait on more than MAXIMUM_WAIT_OBJECTS (64) handles at once.
It's a minor point, but sooner or later someone is going to try to do something really stupid with this class, and wonder why it isn't working as expected.
"So, rather than appear foolish afterward, I renounce seeming clever now."
-- William of Baskerville, The Name of the Rose
|
|
|
|
 |
|
 |
What kinds of tasks did you use this class for?
Todd Smith
|
|
|
|
 |
|
 |
Have a look at www.nitrobit.com. This task is used in DeLogger! and DeSpy! and works proper...
|
|
|
|
 |
|
 |
Hi Frank,
This is a good start, but isn't very robust. A few quick things to think about:
1) The main thread proc only checks the size of the event vector once. What happens if new actions get added after the thread starts?
2) Because the event vector will be accessed from more than one thread, you should protect it with a critical section.
3) Is the intent of this worker thread to a) run queued actions once, or to b) have the actions get spawned multiple times (i.e. triggering the same action to occur multiple times by setting the associated event multiple times)?
If a, you have no way of removing items from the queue.
If b, you have no way of ensuring actions get processed. (Imagine the worker thread is processing some action, and then the main thread wants another action to run several times. It'll call SetEvent on some event multiple times. The action associated with that event will only get called once.) There are other timing issues that can occur because you're using events.
One option would be to have the worker thread run the action as soon as it gets queued.
4) There's no way to remove actions from the queue. One suggestion to fix this is to have AddEvent return an event id. Then you could add a RemoveEvent function to get rid of it.
5) General error checking (e.g. return value from WaitForMultipleObjects, watch out for calling Start() multiple times, etc.)
Ok, that's all for now. Have a good one.
|
|
|
|
 |
|
 |
1. If you look at the code you'll notice that AddEvent asserts if m_Thread is not null.
Todd Smith
|
|
|
|
 |
|
 |
True, there is an assert. This will throw an exception in debug mode, and allow you back into the debugger. Presumably, this will help you code around any errors before making it to release, but what's the harm in handling the error at runtime as opposed to just reporting it?
All a matter of coding philosophy, I suppose.
-keithb
|
|
|
|
 |
|
 |
1) Answered by Tod
2) The event vector is a member of each CWorkerThread object and will be accessed only by the thread once (the first time the ThreadProc build it's handle array to pass to WaitForMultipleObjects(...)).
3) b is intended. An yes if the same event which is processed occurs again AND AGAIN which means set while processing event1 event1 occurs twice it is processes only once again. Thats intended by me because if I process an event I don't need to process it two times again after its finished.
4) No and it doesn't matter 'cause if you don't set the events they will not be processed. Add/Remove is not possible without breaking the event queue and thats not intended...
5) Start is watched 'cause it asserts if the thread is running...
I think you should have a closer look at the code...
cu Frank
|
|
|
|
 |
|
 |
1) Todd answered #5. See my response to him for my thoughts there.
Sorry, I did misread your code--you don't look at the event array after the thread starts. This should be documented, as someone using your class after reading your article could easily assume actions could be added to the list after the thread starts.
2) Ok, since the loop doesn't operate off of the actual array. (Although you should prevent against people calling Reset() while the thread is running. That would cause multiple threads to access it.)
3) You provide this class as a general worker thread implementation. If you make assumptions on how it should be used, they should be documented.
4) It does make less sense to have a remove since you can't add after the thread starts. I disagree with your comment about remove isn't possible without breaking the event queue--this isn't a queue, it's a list of events waiting to get triggered.
5) See #1.
Have a good one.
-keithb
|
|
|
|
 |