Title: Asynchronous general call of MFC member function Author: Author Vladimir Grigoriev Email: grigoriev@arktika.ru , du_volon@fromru.com Environment: VC++ 6.0-7.1, Win2000 Keywords: Thread, MFC Level: "Intermediate" Description: An article on thread call writing optimization Section Miscellaneous SubSection General
About:
How to generally eliminate hang in MFC apps,
converting MFC calls to half -asynchronous,
writing 1 string of code, with no need of disturbing application code, like
writing synchronizations and callbacks,
With
remaining served all usual MFC and application messaging processing, in
not changing existent logic way,
basing on the fact that thread logic are
often of definite type,
and can be implemented separately.
Instead of repetition of thread writing in different each time and not
systematic way, -this article about
Principle:
Based on replacing message pump at calling method and thread waiting , usually hanging place,
eliminating GUI froze.
Better reaction of application and problem methods error processing can be made
much better,
as well thread code more easy written.
Execution scheme: Usual MFC call and with modifying it to auto threaded call
Usually happen problems:
History:
I found that any application hangs at waiting any time consuming resource,
like web, DataBase, system drive,
or just loading other much data.
Demos: As can be seen in MyIe, very good browser, that hangs when error page is
loaded, or when some extended filter, when applied hangs application - it's all
other windows.
Usually that problem is solved by creating thread function,
calling it, synchronizing it with main thread
while it continue their execution - usually with main purpose visual interface initialization.
So design is following - main thread process GUI, as MFC self
intended for work good if no wait there is, and
instead of giving processor ability to be in rest while system must shown,
process time-consuming task in parallel thread. Application became
"reactive" to user interaction and for GUI messages and drawing.
You want "call this member" and wait here and process in
other places , due to it is your usual code
writing method, and better one, so why to
write additional global code, each time, do it different, instead of just call
member with condition of wait, and do
it generally - not loosing time? In one string of code.
So I decided to write code to release second pattern.
Types of use threading in application, usage scenarios:
- "call this member and wait here", BUT proceed GUI messages
that wait time,
- "call this member and no wait" ,
BUT proceed GUI messages
- "call this member and wait some time and continue", BUT proceed GUI messages
last of them usually used for detecting that error happen,
this pattern
process error situation intelligently:
detecting "too long time for that operation is get", but not
total application is hang, but in that isolated call only, and that
may be reported and processing continued.
I write this code for scenario " wait here, BUT proceed GUI messages",
where GUI messages - is typically all that MFC does after initialization, so all
the MFC work,
meaning that if someone who need no proceed GUI messages while wait, can easily
write thread code
self, due to MFC API for that task is usual and simple enough.
if you have MFC call:
BOOL Cauto_threadDlg::OnInitDialog() { int a =5; //member and call it's method: m_else_class . method_1_arg( &a ); //... else code } class some_else_class { public: int method_1_arg(int* j); };
call you can rewrite as:
declaring local variable, with argument: member function type, pointer to
function and
pointer to base class for that called method which is passed to that function as
argument,
so variables within method will be accesses transparently with no need to edit
all within called method,
(that may be already written)
- and as all needed arguments holder. It may be this
pointer, or member of "some_else_class",
if there is need in many arguments.
Also return value type and timeout of wait at place of call.
In a constructor thread will be created and method called, while making possible
to handle
this call waiting (see patterns of use) , or not, or commanding it to
change execution through interface class.
typedef void (some_else_class::*F_def)(int*); CAutoThread<some_else_class, F_def, & some_else_class::method_1_arg, int*, int> local_thread_call( & m_else_class,5000,&a);
Method type:
method must be with one argument - class holding all that need or
"this",
but not VOID and
returning any type except VOID!
Than:
Application continue immediately GUI processing, like message delivery and
reaction is continue to act.
Wait 5 second and continues at place of call- useful for large time detection as
error and make possible to report it.
If call execution finishes before it - application consider that all is normal
and go further from call place.
This is done by replacing windows message pump by new pump
within that variable implementation,
but: automatically for main thread only! Like MFC does.
To make your GUI created in separate thread reacted to input and show changes,
it need to place in the code of called method call to
periodiacally, when you want to show changes,
declared as:
extern void ProcessMessages(HWND hwnd);
hwnd - handle of window that wanted to be message processed for,
or 0 for current thread, where it called.
That gets current messages and dispatches to control for which they are
purposed.
Planned:
You can see how I planned to do it automatically in Planned section, below, and
advice how to do it better,
due this is still some problem. MFC is not thread - safe, but better to be.
Need to remember that call variable created in a sample -is local,
it is need not to define member, so it is works like some SmartPointer,
after loosing scope variable destroy self and no any destruction is need.
I use it for not main classes not dirtying by many declarations and
economy
code writing, "only at needed by logics place", and also avoiding synchronizing
definitions that otherwise can make code not good observable.
But possibility of synchronization across total application is provided:
Main point was also in writing suitable code
that encapsulates all typical thread code in 1 line ,
with parameters that define threaded call.
Making repetitive task of creating threads and some synchronizations not need in
case that are usual for MFC:
eliminating hangs in calls, but also
making synchronizations more easy written in cases where it really need:
when data need be prepared before used:
for that fourth argument in constructor provided:
CAutoThread(T* pT, int
time, argType arg, CEvent_Auto* pCEvent_Auto =0);
so change constructor string to:
local_thread_call(&m_else_class,5000,&a, m_pCEvent_Auto) ;
Get also defined member variable CEvent_Auto* m_pCEvent_Auto
for access to synchronization Event;
which return CEvent_Auto* pointer to object that is usual CEvent
pointer, for use with
standard synchronization code like:
That pointer is allocated from heap and will not be destroyed until you call
CEvent_Auto::Release();
like this:
if(::WaitForSingleObject( *m_pCEvent_Auto,20*000 ) ==WAIT_TIMEOUT) { //Error message -20 seconds - too much for one1 MegaByte file read. // Disk dead to be. CD opened, or programmed bad. } m_pCEvent_Auto->Release(); //....Do your have data prepared - depended processing.
Note: that timer used is not most precise,- threaded timer precision is used
plus
waiting for message Dispatch time, so if you have more hanging calls, timer will
reflect
that hang period.
So can be detected call with probable hang at other places of application.
Calling methods within thread can help much to better respond to error
situation,
than hang that sometimes can happen if no exception generated and problem method
called is hanged.
You have all access into within thread providing your class as argument of
the method that is threaded,
inform it by setting some its member so it can exit its execution in a thread,
calling within ExitThread(),
or changing its execution to better way than asking problem device, that is to be
generated error,
or you can terminate thread externally:
Thread pointer can be than get by call:
CWinThread* GetThread()
and terminated calling
TerminateThread( CWinThread);
note than this call is considered in all documentation as not safe for stack
clearance due to their forcing character,
but useful in serious situation.
Usual checking result procedure can be written as folows:
Call is defined as:
Locally created variable - template instantiation,
that creates working thread, in which desired member function is called.
template <class T,class typeOf_Fn, typeOf_Fn f,class argType, class RetValType > class CAutoThread
constructor:
CAutoThread(T* pT, int time, argType arg, CEvent_Auto* pCEvent_Auto =0, DWORD dwCreateFlags =0)
class
T
- owing class called member function ("this" class)
class
typeOf_Fn - type of member function, or it's signature
typeOf_Fn f
- pointer to member function,
class argType
- type
of class that will be passed as single argument, holding all others in need.
class RetValType
-
type o return value.
argType arg - instance of argument class, usually better
to use only pointer, to avoid unneeded coping,
that also can be error prone.
T* pT
- instance of owing clas of
called method.
int time
- timeout
to wait method execution at place of call.
argType arg - argument that must be passsed to method.
CEvent_Auto* pCEvent_Auto - returned pointer to syncronization CEvent.
DWORD dwCreateFlags - can be 0 to run immediately or CREATE_SUSPENDED
to create thread suspended and than Run, at needed time bu calling
than CAutoThread::RunThread() :
to make it possible to define as member
variable of AutoThread in a some some holding class, or not run execution of method by else reason, but
design goal not propose reasonable possibility for it,
due to not public CWinThread* variable is planned to use - in this case better
to define thread by self than to specialize all.
Usage:
BOOL Cauto_threadDlg::OnInitDialog() {int a =5; typedef void (some_else_class::*F_def)(int*); CAutoThread<some_else_class, F_def, & some_else_class::method_1_arg, int, 5> local_thread_call (&m_else_class, 3000, a) ; }
class Cauto_threadDlg { //... some_else_class m_else_class; };
class some_else_class { public: void method_1_arg(int* j) { AfxMessageBox("Threaded Member Called with 1 arg"); CreateGUI(); //see more in detail at sample for(int i=0;i< *j; i++) { //Waiting Call ::Sleep(1000); // make GUI reacting: ProceesMessages(); } return ; };
Planned:
Still is need to call this function periodically within thread planned to be
made better reactive, but I plan to implement automatic thread GIU redrow
assistance,
for the sake this is done in a displacingpreemptive way( win32), instead of
cooperative, when you need to care about this self as proposed now. To place
mesage processing to separate thread insted of "all in one" as in MFC
currently, like in win16.