|
Thank you very much, Good job!
|
|
|
|
|
Useful stuff Amere.
Pozdrav :?
|
|
|
|
|
|
First of all, this is a very usefull class and I thank you for posting it.
I have had problems in MFC displaying a progress bar with status of a background long running task. My progress bar was placed on a dialog window with a Cancel button and a static control (to display some status text). The problem was that the user was unable to click on the Cancel button as if the Cancel button was disabled. It was becuase the dialog window and the actuall processing was taking place on the same thread. Then I had to do lot of "work-around" to fix it, even then it was problemetic. Then I saw the
BackgroundWorker[^] class in C#.
I modeled a class in MFC like BackgroundWorker class in C#. Then I was able to get all information (i.e., if the task is completed or the percent completed or if the use has cancelled the task etc.)
Best regards,
Mizan Rahman
|
|
|
|
|
Yes, BackgroundWorker is very useful class. It is an alternative way of performing parallel tasks.
Thanks
|
|
|
|
|
BOOL CAG_Thread::StartThread(HWND hWnd)
{
// if thread already running do not start another one
if(GetThreadStatus())
return FALSE;
// create stop event
m_hStopEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if(m_hStopEvent == NULL)
return FALSE;
// create thread
DWORD dwThreadId;
m_hThread = ::CreateThread(NULL, 0, ThreadMain, this, 0, &dwThreadId);
if(m_hThread == NULL)
return FALSE;
//set thread Priority
// SetThreadPriority(m_nPriority); //moe
m_dwThreadID = dwThreadId;
return TRUE;
}
bool CAG_Thread::SetThreadPriority(int priority)
{
return SetThreadPriority(priority);
//moeTHREAD_PRIORITY_TIME_CRITICAL
}
I implement thread function changin thread priority as abouve but unsuccesfully.... What's the problem
P.S If u put AfxMessage box after in if(ShouldDie()) app will not stop thread.
^WINX^
|
|
|
|
|
winxalex wrote:
P.S If u put AfxMessage box after in if(ShouldDie()) app will not stop thread
I am not sure how to answer this, but it works on my machine. I guess I
don't understand why it would not work on yours.
Regarding the thread priority, I hope you are not using the code above
because it would run into infinite loop (recursive call). For SetThreadPriority first parameter is handle to the thread.
Otherwise I would need more info on how did you confirm that setting thread priority does not work. Keep in mind that in my example the thread is mostly sleeping (doing nothing) so that it does take almost any processing time, so that any thread priority would seem to be the same.
Here is what I tried real quick:
In StartThread(...) I inserted just before return statement:
if(::SetThreadPriority(m_hThread, THREAD_PRIORITY_TIME_CRITICAL) == 0)
{
StopThread();
return FALSE;
}
and in myThread class in ThreadMain():
UINT myThread::ThreadMain()
{
// init progress control
m_pProgress->SetRange(0, 100);
m_pProgress->SetPos(0);
for(int i=0; i<101; ++i)
{
if(ShouldDie())
return 1;
// Verify the threads priority !!!
ASSERT(GetThreadPriority(m_hThread) == THREAD_PRIORITY_TIME_CRITICAL);
m_pProgress->SetPos(i);
Sleep(100);
}
return 0;
}
I did not have assertion!
Also I used process viewer to verify that my newly created thread priority is 15.
I am running Win2K SP4, with VC++ 6.0 Prof. SP5
I hope this will help
Thanks
Amer G.
|
|
|
|
|
chudno boolaaaaaaan. and Thx for so fast replay. That for 5. )
It's weird. That not Assert. How can u see thread priority in process viewer. U can see there only process priority which is not the same. All threads in your app are in one proccess let say exe.
if(ShouldDie())
{
AfxMessageBox("mujo i haso");
return;
}
AfxProblem can be maybe because of thread is already realsed or maybe because i don't have SP5
|
|
|
|
|
|
Hi,
i try to use waitForSingleObject with this class, but don´t know what i have to
choose as thread handler.
Set "m_hThread" as public in CAG_Thread and call:
WaitForSingleObject(m_myThread.m_hThread,INFINITE);
give me no debug message, but also never ends
Any ideas ?
|
|
|
|
|
I am not sure if I am understanding your question correctly, but
to check if the thread is still running you could use the
memeber function BOOL GetThreadStatus() which returns TRUE
if the thread is still running or FALSE if the thread terminated.
Look at the implementation of this function, it is very simple.
Otherwise the WaitForSingleObject is used from inside the thread
to catch the request for termination of the thread from outside
world. The function BOOL ShouldDie() is cheking if the event
for termination is set and returns TRUE if user wants to terminate
thread. But it is then users responsibility to check for this event
using the same function from inside the ThreadMain function. See
the comment in the header file.
I hope this helped.
Thanks
Amer Gerzic
|
|
|
|
|
I think my description wasn´t exactly enough. I try it again
I have a main programm which has to analyse some files one after another in a thread.
I try to do this in the following way (code is simplified!):
main::OnButtonClick{
INT AmountFiles = 5;
for (int i=0; i<AmountFiles; i++)
{
StartThread();
while(m_Thread.GetThreadStatus()); //wait
doSomethingElse();
}
}
My Problem is how to realize: while(GetThreadStatus()); //wait
The whole main program freeze as long as the thread hasn´t finished.
I´m very new to threads and maybe i have the wrong basic idea of handling such things
Thanks
Sickboy
|
|
|
|
|
Ok, I see now what you mean (I hope).
First of all it depends what you want to do. When you say
you need to analyze 5 files in a thread, you have to ask yourself
do these operations depend on each other? If you would to
run the above code without the WaitForSingleObject line
you would create 5 threads that would run parallel with
each other and parallel to main process thread.
If each file is analyzed in the same way, derive new class
from the CAG_Thread and override the ThreadMain. In thread main
analyze file. ThreadMain is executed in separate thread and independently
from everything else. Keep in mind that in the processing loop
chack occasionlly if ShoudDie() returns true. In this way you can
abort the thread execution before it is done (If you nee/want to do so)
If each operation depends on previous one then you would use single
thread to process all files. Derive new class from CAG_Thread and
override the ThreadMain function. But now analyze all 5 files
in this ThreadMain just like you would do in any other function. Now
create only one thread object and start only one thread parallel to
your main thread. Also there is HWND argument which you could use
to pass the handle of your main window with which you could
send messages from your thread (when it is done or something else)
Hope this helps!
Amer Gerzic
|
|
|
|
|
Very interesting indeed!!
After looking at your code, it tells me you have a lot of creativity running around in your head. I also noticed you had to do all the work pertaining to the thread, yourself (despite this being an MFC application).
I would like to get into some of those observations later, but for right now, tell me, "Where does 'ThreadMain' (the static function inside class CAG_Thread) gets its 'lparam' parameter value?" The function is called with that value. From where does it get it?
William
Fortes in fide et opere!
|
|
|
|
|
Just look at the implementation of the StartThread function in AG_Thread.cpp. In this function I am calling win32 CreateThread(...) function. If you look in the MSDN docs, you will find that one of the parameters is passed as the parameter to the thread function, which in turn is my static ThreadMain function. What I am passing as parameter is the pointer to the thread object itself (this), so that I can cast it in ThreadMain function and call virtual ThreadMain. Here is the code:
<br />
DWORD dwThreadId;<br />
m_hThread = ::CreateThread(NULL, 0, ThreadMain, this, 0, &dwThreadId);<br />
if(m_hThread == NULL)<br />
return FALSE;<br />
The third parameter is the thread function (ThreadMain), and 4th parameter is parameter to be passed to the thread function (this).
I hope this will help you!
|
|
|
|
|
Thanks. Your explanation was clear.
What threw me for a while was that both functions had the same name (the static one as well as the virtual one), and knowing that the virtual one must be overriden by a function with the same name in the class derived from CAG_Thread, I thought the "this" pointer was referring to the class object for that one (the derived one), which in a way it is, even as you explained it.
After changing those names and making them unique, things began falling into place immediately.
I also made a cosmetic change to the sample so that if the user were to let the progress bar run its full course, the message button would change back to "Start Thread", and not left stuck on "Stop Thread". Besides, it seemed more intuitive that way too.
Lastly, though I have not done so as yet, I intend to try certain variations of techniques on your sample such as using AfxBeginThread instead of the CreateThread API as you did. I also noticed in your use of the API, you had to do everything for the thread which AfxBeginThread would have done safely and automatically for you.
Then there is the use of the "CreateThread" member function of CWinThread which allows the user to create a single thread and reuse it between successive creation and termination of thread executions. IOW, multiple executions for the same thread object; no need to keep creating and deleting the thread object.
All in all, I enjoyed the positive stimulation I received from reviewing your sample. Good job!!
William
Fortes in fide et opere!
|
|
|
|
|
|
Hello!
I read your article and want to point you to another simple imlementation
of the worker thread - by Bjarke Viksoe.
This is just a same code in some points, but has a some more useful moments.
I never know when i begin a new piece of work - will i use CRT code ?
Using a compile time checking is a good thing for me, so look at the Create method and you will see what i say about.
The other code by Bjarke you can find at viksoe.dk, i just include all his CThread code for speed.
I use this code in many places...
Thanks for article !
Stanislav.
#if !defined(AFX_THREAD_H__20000927_7992_09B2_A0EF_0080AD509054__INCLUDED_)
#define AFX_THREAD_H__20000927_7992_09B2_A0EF_0080AD509054__INCLUDED_
#pragma once
/////////////////////////////////////////////////////////////////////////////
// Thread - wrapper for Thread API
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//
#if defined(_MT) || defined(_DLL)
// Due to the nature of the multithreaded C runtime lib we
// need to use _beginthreadex() and _endthreadex() instead
// of CreateThread() and ExitThread(). See Q104641.
#include <process.h>
#endif
#ifndef ATLTRY
#define (x)
#endif
/////////////////////////////////////////////////////////////////////////////
// CThread
template<bool t_bmanaged="">
class CThreadT
{
public:
HANDLE m_hThread;
bool m_bSuspended;
CThreadT() : m_hThread(NULL), m_bSuspended(false)
{
}
virtual ~CThreadT()
{
if( t_bManaged ) Release();
}
BOOL Create(LPTHREAD_START_ROUTINE pThreadProc,
LPVOID pParam=NULL,
int iPriority=THREAD_PRIORITY_NORMAL)
{
_ASSERTE(m_hThread==NULL);
_ASSERTE(pThreadProc);
DWORD dwThreadID;
#if defined(_MT) || defined(_DLL)
m_hThread = (HANDLE) _beginthreadex(NULL, 0, (UINT (WINAPI*)(void*))pThreadProc, pParam, 0, (UINT*)&dwThreadID);
#else
m_hThread = ::CreateThread(NULL, 0, pThreadProc, pParam, 0, &dwThreadID);
#endif
if( m_hThread==NULL ) return FALSE;
if( iPriority!=THREAD_PRIORITY_NORMAL ) {
if( !::SetThreadPriority(m_hThread, iPriority) ) {
_ASSERTE(!"Couldn't set thread priority");
}
}
return TRUE;
}
BOOL Release()
{
if( m_hThread==NULL ) return TRUE;
if( ::CloseHandle(m_hThread)==FALSE ) return FALSE;
m_hThread = NULL;
return TRUE;
}
void Attach(HANDLE hThread)
{
_ASSERTE(m_hThread==NULL);
m_hThread = hThread;
}
HANDLE Detach()
{
HANDLE hThread = m_hThread;
m_hThread = NULL;
return hThread;
}
BOOL SetPriority(int iPriority) const
{
_ASSERTE(m_hThread);
return ::SetThreadPriority(m_hThread, iPriority);
}
int GetPriority() const
{
_ASSERTE(m_hThread);
return ::GetThreadPriority(m_hThread);
}
BOOL Suspend()
{
_ASSERTE(m_hThread);
if( m_bSuspended ) return TRUE;
if( ::SuspendThread(m_hThread)==-1 ) return FALSE;
m_bSuspended = true;
return TRUE;
}
BOOL Resume()
{
_ASSERTE(m_hThread);
if( !m_bSuspended ) return TRUE;
if( ::ResumeThread(m_hThread)==-1 ) return FALSE;
m_bSuspended = false;
return TRUE;
}
BOOL IsSuspended() const
{
_ASSERTE(m_hThread);
return m_bSuspended==true;
}
BOOL IsRunning() const
{
if( m_hThread==NULL ) return FALSE;
DWORD dwCode = 0;
::GetExitCodeThread(m_hThread, &dwCode);
return dwCode==STILL_ACTIVE;
}
BOOL WaitForThread(DWORD dwTimeout=INFINITE) const
{
_ASSERTE(m_hThread);
return ::WaitForSingleObject(m_hThread, dwTimeout);
}
BOOL Terminate(DWORD dwExitCode = 0) const
{
// See Q254956 why calling this could be a bad idea!
_ASSERTE(m_hThread);
return ::TerminateThread(m_hThread, dwExitCode);
}
BOOL GetExitCode(DWORD* pExitCode) const
{
_ASSERTE(m_hThread);
_ASSERTE(pExitCode);
return ::GetExitCodeThread(m_hThread, pExitCode);
}
#if(WINVER >= 0x0500)
BOOL GetThreadTimes(LPFILETIME lpCreationTime, LPFILETIME lpExitTime, LPFILETIME lpKernelTime, LPFILETIME lpUserTime) const
{
_ASSERTE(m_hThread);
_ASSERTE(lpExitTime!=NULL && lpKernelTime!=NULL && lpUserTime!=NULL);
return ::GetThreadTimes(m_hThread, lpExitTime, lpKernelTime, lpUserTime);
}
#endif
operator HANDLE() const { return m_hThread; }
};
typedef CThreadT<false> CThreadHandle;
typedef CThreadT<true> CThread;
/////////////////////////////////////////////////////////////////////////////
// CThreadImpl
class CThreadImpl : public CThread
{
public:
bool volatile m_bStopped; // Signals when thread should stop (thread loop should exit)
CThreadImpl() : m_bStopped(false)
{
}
virtual ~CThreadImpl()
{
// NOTE: Remember destructors cannot call overrides!
Stop();
}
virtual BOOL Start()
{
m_bStopped = false;
if( !Create(ThreadProc, (LPVOID)this) ) return FALSE;
return TRUE;
}
virtual void Stop()
{
if( SignalStop()==FALSE ) return;
WaitForThread();
Release();
}
BOOL SignalStop()
{
if( m_hThread==NULL ) return FALSE;
m_bStopped = true;
if( m_bSuspended ) Resume();
return TRUE;
}
BOOL ShouldStop() const
{
_ASSERTE(m_hThread);
return m_bStopped==true;
}
static DWORD WINAPI ThreadProc(LPVOID pData)
{
CThreadImpl* pThis = static_cast<cthreadimpl*>(pData);
#if defined(_MT) || defined(_DLL)
ATLTRY(_endthreadex( pThis->Run() ));
return 0;
#else
DWORD dwRet = 0;
ATLTRY(dwRet = pThis->Run());
return dwRet;
#endif
}
virtual DWORD Run()
{
_ASSERTE(false); // must override this
/*
// Sample thread code...
while( !ShouldStop() ) {
...
}
*/
return 0;
}
};
/////////////////////////////////////////////////////////////////////////////
// CEvent
class CEvent
{
public:
HANDLE m_hEvent;
CEvent() : m_hEvent(INVALID_HANDLE_VALUE)
{
}
~CEvent()
{
Close();
}
BOOL Create(LPCTSTR pstrName, BOOL bManualReset=FALSE, BOOL bInitialState=FALSE, LPSECURITY_ATTRIBUTES pEventAttributes=NULL)
{
_ASSERTE(!::IsBadStringPtr(pstrName,-1));
_ASSERTE(m_hEvent==INVALID_HANDLE_VALUE);
m_hEvent = ::CreateEvent(pEventAttributes, bManualReset, bInitialState, pstrName);
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return m_hEvent!=INVALID_HANDLE_VALUE;
}
BOOL Open(LPCTSTR pstrName, DWORD dwDesiredAccess=EVENT_ALL_ACCESS, BOOL bInheritHandle=TRUE)
{
_ASSERTE(!::IsBadStringPtr(pstrName,-1));
_ASSERTE(m_hEvent==INVALID_HANDLE_VALUE);
m_hEvent = ::OpenEvent(dwDesiredAccess, bInheritHandle, pstrName);
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return m_hEvent!=INVALID_HANDLE_VALUE;
}
BOOL IsOpen() const
{
return m_hEvent != INVALID_HANDLE_VALUE;
}
void Close()
{
if( m_hEvent==INVALID_HANDLE_VALUE ) return;
::CloseHandle(m_hEvent);
m_hEvent = INVALID_HANDLE_VALUE;
}
BOOL ResetEvent()
{
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return ::ResetEvent(m_hEvent);
}
BOOL SetEvent()
{
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return ::SetEvent(m_hEvent);
}
BOOL PulseEvent()
{
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return ::PulseEvent(m_hEvent);
}
BOOL WaitForEvent(DWORD dwTimeout=INFINITE)
{
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return ::WaitForSingleObject(m_hEvent, dwTimeout)==WAIT_OBJECT_0;
}
operator HANDLE() const { return m_hEvent; }
};
#endif // !defined(AFX_THREAD_H__20000927_7992_09B2_A0EF_0080AD509054__INCLUDED_)
|
|
|
|
|
|
Nice article . It's always interesting to see how people approach threading. Dot NET may change all that but the old Win32 API was not a model of clarity. I would probably latch right onto your stuff but just got through looking at CWorkerThread and playing with various permuations and I think it is a gem - though almost undocumented. Couldn't find sample code except the link here. Fortunately, the class source is well documented and clean.
These ready built templates in ATL 7.0+ provide easy thread handling, even doing things like shutting them down hard if need be. CWorkerThread<>, the one I played with, is described with a short, clever program in this article on codeproject.
http://www.codeproject.com/atl/newinatl7.asp?target=cworkerthread
ATL also has a threadpool (CThreadPool<>) class that looks easy to use if you have to spin off multiple threads. There is MSFT sample code for it. The documentation in in the "ATL Server Classes"
-Marty
|
|
|
|
|
Thanks,
Don't you hate the undocumented stuff? It is like unsorted phone book. You spend hours to find one simple thing.
.NET will change the way we program today. Is it going to be good or bad change, we will see in the future, probably both. However I do not have a lot of ATL experience. I did play around with it just for fun for a while, mostly with ATL 3 and COM programming, but I didn't use it in any applications that I wrote. So far I liked it. I will check out the CWorkerThread<...> class.
|
|
|
|
|
Yup, undocumented stuff is a pain but then I remember the terse C Manual Whitesmiths put out (CP/M 8080 early 80's) and count my blessings.
Something I forgot to mention is that ATL.NET has decoupled many useful classes from ATL projects and they can be used in nearly any C++ program. The workerthread is one of those. Another is the Regular Expression classes and am using them instead of the Boost lib which, while quite good, doesn't have the integrated help.
ATL is also a good place to get familiar with things like template style "static virtualization" and other tricks that can sometimes produce really high quality, fast code. It's commented more than the STL and is a great place to learn template programming technique.
-Marty
|
|
|
|
|
Very well first work, Amer
It is a clean and well written article!
Working with threads for about 8 years now, I have also developed and used a variety of thread classes and utilities to make using threads as simple as possible. Some have been very lightweight, like yours, others have been rich of features for synchronisation, message passing and so on. If you don't mind, I would like to share and discuss my experience with you.
Over the years I came finally to the conclusing that inheritance is, in general, not an ideal approach to provide the functionality of concurrency (let a specific method be executed in an own thread). It brings a lot of limitations and drawbacks. Some of them are:- Unwanted substitutability: OO teaches us to use inheritiance if we want to model a "kind-of" relationship between two classes. The more specific class (
myThread ) can then be used as a substitute for the more generic class (CAG_Tread ). However, dealing with threads this is usually not our intention. We don't want to create a new kind of thread, we want to use the functionality of threads in our own class. - Structural impacts: In every non-trivial case the class you want to utilize threads already inherits from another class. Consequently you have to use multiple inheritance. However, using multiple inheritance can lead to structural impacts, as a result a lot of developers intentionally avoid it.
- Limited to one thread per class: The inheritance approach is limited to one thread per class. What, if we want two methods to be executed in its own thread?
- ...
Conceptually speaking, concurrency is not an attribute of a class, it is an attribute of a method. We want a specific method to be executed in its own thread. Some languages (like ADA) provide specific classifiers for this. Therefore class inheritance could not be the optimal way to go.
The solution I finally came to is to go back to function pointers and the good old callback mechanism. (Or to use a more OO-like and modern term for the very same concept: Delegates) Of course it needs some tricks to make CreateThread() and _beginthreadex() accepting real member functions (instead of static functions and ordinary C functions) as thread starters. However, it is possible and the result makes dealing with treads really easy:
#include "stdafx.h"
#include "win_adapter.h"
class Test
{
public:
Test()
{
LPCTSTR pszThreadName = "Joe, the funny thread";
HANDLE hThread1 = win::beginthreadex( this, &Test::SomeFunction, pszThreadName );
HANDLE hThread2 = win::beginthreadex( this, &Test::OtherFunction );
}
DWORD SomeFunction( LPCTSTR pszName )
{
printf( " Hello, I'm a Thread and my name is: %s\n", pszName );
return 0;
}
DWORD OtherFunction( int )
{
...
return 0;
}
};
The technical concepts behind the "magic" win::beginthreadex() function are explained in my article Use member functions for C-style callbacks and threads - a general solution
What do you think about it?
--
Daniel Lohmann
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there )
|
|
|
|
|
Thanks Daniel,
I agree with you on all of the points. As a matter of fact, in the past, I found myself in situations where I had same problems, but because they were rare, I did not look for a better solution. I did not read your article yet, but I certainly will, it seems to me that you have a lot more experience in this field than I do. However, regarding my code, what I prefer always is simplicity. What I mean with that is the fact that I like to write classes, which perform only single simple task. So the fact that the class can have only single thread, I see as advantage. Simple small classes are easier to maintain. This is probably due to the fact that I maintain some code in the company that I work for. The projects here that I maintain include a lot of unnecessary code, which makes maintenance harder, but do not contribute to the quality of the code, and sometimes do not add to the application extensibility. One example would be of having a piece of code that would perform simple function in separate thread without the need for synchronization, but still having all synchronization nuts and bolts in it.
I guess it all boils down the question, what do I need and what will I need in the future (which is hardest to answer in the real world where the clients are bosses)? And finally I must add that with this comment, I am not saying that the way you use threading is more complicated way. Actually it looks pretty simple (from the code above) and makes sense, so I am very anxious to read your article (I need to find some time first between all these final exams I have).
|
|
|
|
|
Amer Gerzic wrote:
I guess it all boils down the question, what do I need and what will I need in the future (which is hardest to answer in the real world where the clients are bosses)?
Very true
You are definitely right with your favor of simplicity. I had written several feature-rich classes (not only, but also for threading) and had to find out that other developers just don't use them. They seem to be too complicated. Aem, to be honest I also did not reuse them that much.
Today I am very serious of making my stuff easy usable. Expecially I am taking care of introducing as few structural implications as possible (classes to derive from, additional headers, libs, etc.) That's why I came a bit over introducing functionality via inheritance - it causes a lot of structural implications.
Viel Glück für Deine Prüfungen!
--
Daniel Lohmann
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there )
|
|
|
|
|