Click here to Skip to main content
Click here to Skip to main content

Using Worker Threads

By , 5 Apr 2001
 

Introduction

Worker threads are an elegant solution to a number of problems about concurrent processing; for example, the need to keep the GUI active while a computation is being performed. This essay addresses some of the issues of using worker threads. A companion essay talks about some techniques dealing with user-interface threads.

Why Worker Threads?

Consider a simple implementation of a program. For our purposes, the program has the task of inverting the color of every pixel of an image, just because we sometimes need a concrete example to illustrate techniques. For the sake of our example, we will assume that the image being processed is 10 megapixels of 24-bit color.

The GUI has a menu item or other means of saying "Invert it now". This calls the doInvert method on the view class:

void CMyView::doInvert()
    {
     for(int x=y = 0; y < image.height; y++)
          for(int x = 0; x < image.width; x++)
              changePixel(x, y);
    }

This is a perfectly correct program. It traverses all 10 megapixels, happily changing the pixels, until it completes. But it is not a good implementation.

Why not? Because the entire GUI is dead until the operation completes. This means that for some long duration, the user is forced to wait while the operation proceeds, and there is absolutely nothing that can be done about it. If the user decides that the transformation is bogus, and wants to stop it, well, tough. It is going to complete anyway.

Doing it the obsolete, and hard, way

One solution, the antiquated and largely unused 16-bit Windows solution (but still used because it is "well known"), is to use PeekMessage, an API call that does not block when there is no message.

void CMyView::doInvert()
    {
     running = TRUE; 
     for(int x=y = 0; running && y < image.height; y++)
          for(int x = 0; running && x < image.width; x++)
              { /* change it */
               MSG msg;
               if(PeekMessage(&msg, AfxGetMainWnd()->m_hWnd,
                              0, 0, PM_REMOVE))
                   { /* handle it*/
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                   } /* handle it */
               changePixel(x, y);
              } /* change it */
    }

This is bad for several reasons. The most important one is that it puts, in the time-critical main loop, a function whose overhead is substantial. Now, instead of taking k minutes (whatever that was before) we might find the algorithm takes a significant multiple of that to complete. But while it is running, the GUI is still active. You could even, if you were not careful, fire off another thread to paint each green pixel purple. This Is Not A Good Idea.

The performance hack is simple: only poll occasionally. For example, if we assume that the images are roughly rectangular, we could change the code to

void CMyView::doInvert()
    {
     running = TRUE; 
     for(int y = 0; running && y < image.height; y++)
         { /* do one row */
          MSG msg;
          if(PeekMessage(&msg, AfxGetMainWnd()->m_hWnd,
                         0, 0, PM_REMOVE))
             { /* handle it*/
              TranslateMessage(&msg);
              DispatchMessage(&msg);
             } /* handle it */
 
          for(int x = 0; running && x < image.width; x++)
              { /* change it */
               changePixel(x, y);
              } /* change it */
         } /* do one row */
    }

Thus this tests only once per row, which can either be too often if the rows are short, or not often enough if the rows are long. The generalization, changing the test to

if(x % 10 == 0 && PeekMessage(...))

will work if the rows are too short.

There are some problems remaining; for example, if there is a modeless dialog active, you will notice that there is no IsDialogMessage call there to handle it. Oops. Nor, for that matter, is there code to handle ActiveX event notifications. Oops. And it presumes that you are writing in pure C, and doing the dispatch yourself. Life gets more complicated when you have to support the message maps and control paradigms of MFC. Oops, squared.

But why bother when there is a better way?

The Thread Solution

It is almost always the case that you can use threads to do the job more easily. This is not without certain costs and hazards, but it ultimately is the better method.

Here's a solution to handling the invert-everything. Note that we have to move from the pure-C domain (which we interface to via a static method) to the MFC domain.

To the class (in this example, a CView-derived class), add the following declarations:

static UINT run(LPVOID p);
void run();
volatile BOOL running;

To start a thread, your handler does

void CMyView::doInvert()
    {
     running = TRUE;
     AfxBeginThread(run, this);
    }
 
UINT CMyView::run(LPVOID p)
    {
     CMyView * me = (CMyView *)p;
     me->run();
     return 0;
    }
 
void CMyView::run()
   {
     for(int x=y = 0; running && y < image.height; y++)
          for(int x = 0; running && x < image.width; x++)
              changePixel(x, y);
    running = FALSE;
   }

The command to stop the thread is very simple:

void CMyView::OnStop()
   {
    running = FALSE;
   }

That's all there is to it!

Well, almost. Keep reading.

For example, the above code assumes that the thread will not try to access any member variables of the view unless it is certain they exist. This includes handles to synchronization primitives or pointers to a CRITICAL_SECTION that might be used to synchronize interactions between the thread and its view. This requires a more graceful shutdown mechanism.

Note that the declaration of the running variable includes the modifier volatile. This is because under certain optimizations, the compiler will discover that in the body of the loop there is absolutely nothing that changes the running flag, and therefore, cleverly, it can avoid testing it each time through the loop. This means that although you change the value in another thread, the change is never seen. By adding the volatile modifier, you tell the compiler that it cannot assume the variable will remain unmodified during the execution of the loop, even though there is no code in the loop that can change the variable.

Worker threads and the GUI I: Enabling controls

The problem is that when your worker thread is running, there are probably lots of things you shouldn't be doing. For example, starting a thread to do a computation. Then you'd have two threads running doing the same or similar computations, and that way madness lies (we'll assume for the moment that this actually is a Bad Thing).

Fortunately, this is easy. Consider your ON_UPDATE_COMMAND_UI handler

void CMyView::OnUpdateInvertImage(CCmdUI * pCmdUI)
    {
     pCmdUI->Enable(!running && (whatever_used_to_be_here));
    }

Generalizing this to cover other menu items is left as an Exercise For The Reader. However, note that this explains why there is that assignment running = FALSE; at the end of the thread handling routine above: it explicitly forces the running flag to reflect the running status of the thread. (Well, if you are being very pedantic, note that it is possible to start another thread before the current one finishes if the current one does not quickly get back to test the running flag, so you may wish to use a separate Boolean variable to indicate the thread state. Set it before the thread starts, and clear it only after the thread loop completes. For most thread usages, a single running flag usually suffices.

Worker threads and the GUI II: Don't touch the GUI

That's right. A worker thread must not touch a GUI object. This means that you should not query the state of a control, add something to a list box, set the state of a control, etc.

Why?

Because you can get into a serious deadlock situation. A classic example was posted on one of the discussion boards, and it described something that had happened to me last year. The situation is this: you start a thread, and then decide to wait for the thread to complete. Meanwhile, the thread does something apparently innocuous, such as add something to a list box, or, in the example that was posted, calls FindWindow. In both cases, the process came to a screeching halt, as all threads deadlocked.

Let's analyze these two situations in some detail, so you get a sense of what happened.

In my case, the list box sent a notification, via SendMessage, to its parent. This means the message went to its parent thread. But the parent thread was blocked, waiting for the thread to complete. But the thread couldn't complete until it could run, and guess what: the SendMessage was a cross-thread SendMessage, which would not return until it was processed. But the only thread that could process it was blocked, and couldn't run until the thread completed. Welcome to the world of deadlock.

The FindWindow problem was quite similar. The programmer had specified finding a window with a particular caption. This meant that the thread running FindWindow had to SendMessage a WM_GETTEXT message to the window whose handle it had just found via EnumWindows. This message could not be processed until the thread that owned the window could execute. But it couldn't, because it was blocked waiting for the thread to finish. Deadlock. So note that although you should not touch the GUI thread explicitly, you must also not touch it implicitly, through such innocuous-seeming operations such as FindWindow, or you can, and will, experience deadlock. Under these conditions, by the way, there is no recovery. You can kill the process, which explicitly terminates all threads, but this is neither elegant nor, as the warning box tells us, necessarily safe.

How do you get around these problems?

In my case, I had just been sloppy. I actually know better, and have written as much. So much for being an expert. The workaround in almost all cases is that you must never to a SendMessage to a GUI object. In very rare cases, you can do a PostMessage, although this usually won't work because you need to pass in a pointer to a string, and cannot tell when the operation has finished so you can't tell when to release the string. Any string you pass in must be allocated either in static storage (as a constant), or on the heap. If it is allocated in writeable static storage, or on the heap, you need to know when the value can be reused or freed. PostMessage does not allow you to do this.

Therefore, the only solution is to use a user-defined message, and post it to the main GUI thread (usually the main frame, but frequently a CView-derived object). The main GUI thread then handles the SendMessage to the GUI object, and, knowing then when the operation has completed, is able to free up any resources.

You must not send a pointer to a stack-allocated object in a PostMessage call. By the time the operation begins to execute, the chances are excellent that the object will have been removed from the stack. The results of this are Not A Pretty Sight.

This essay will not go into describing how to manage user-defined messages. That is covered in my essay on Message Management. We will assume that there are some user-defined messages, with names like UWM_ADDSTRING, that are already defined.

Here's some code that adds window names to a list box:

void CMyDialog::AddNameToControl(CString & name)
    {
     CString * s = new CString(name);
     PostMessage(UWM_ADDSTRING, 0, (LPARAM)s;
    }

To the collection of handlers in CMyDialog.h, I add the declaration

afx_msg LRESULT OnAddString(WPARAM, LPARAM);

To the MESSAGE_MAP of my class, I add

ON_REGISTERED_MESSAGE(UWM_ADDSTRING, OnAddString);

or

ON_MESSAGE(UWM_ADDSTRING, OnAddString)

(If you're curious about the distinction, read my essay on Message Management).

The handler then looks like this:

LRESULT CMyDialog::OnAddString(WPARAM, LPARAM lParam)
    {
     CString * s = (CString *)lParam;
     c_ListBox.AddString(*s);
     delete s;
     return 0;
    }

The sender of the message must allocate the value being sent on the heap, which it does via the new operator. The message is posted to the main GUI thread, which eventually routes it to the OnAddString handler. This particular handler knows that the message is destined for a particular control, c_ListBox (if you don't understand how to get control variables for controls, read my essay on the subject). Note that we could have put the destination control ID, or the destination CWnd-class variable, in wParam, and made this more general. The handler calls the AddString method. When this call completes, it is now known that the string value is no longer required (this would be different if we had an owner-draw listbox without LBS_HASSTRINGS, but if you already know how to do that, the solution should be evident). Therefore, we can now delete the heap-allocated object, which for us is a CString.

Worker Threads and the GUI III: Dialogs and MessageBoxes

You must not try to launch any GUI window from a worker thread. This means a worker thread cannot call on MessageBox, DoModal, Create of a modeless dialog, and so on. The only way you can handle this is to post a user-defined message back to the main GUI loop to perform this service for you.

If you attempt to do this, you will get various odd failures. You will get ASSERT errors from MFC, dialogs will fail to come up, and essentially you will get all sorts of antisocial behavior from your application. If you really, really need to have GUI objects operating from a thread, you must not use a worker thread. You must use a user-interface thread, which has a message pump. I'm not an expert on these, although I've done a brief article on what I discovered about them while doing a SAPI application, and this may be of some assistance in using them.

If you must pause the thread until the dialog or MessageBox completes, you should probably use an Event for synchronization. What you would do is create, for example, an auto-reset Event, which is created in the Cleared state. Post a user-defined message (see my article on Message Management) to the main GUI thread, which will then launch the dialog. When the dialog completes, it calls ::SetEvent to set the event. Meanwhile, the thread, after posting the message, uses ::WaitForSingleObject to wait for the event to be set. The thread will block until the dialog completes and sets the event.

I really should check the VC++ 6.0 implementation of CEvent. The VC++ 4.2 implementation was so full of errors as to be totally unusable. You Have Been Warned.

Worker Threads and the GUI IV: AfxGetMainWnd

Well, I've shown how to post a message to the window for the class. But what if you want to post a message to the main window? This is obvious, right? Just do

AfxGetMainWnd()->PostMessage(...)

and you're done! Right? Of course not. It should be obvious by now that I wouldn't pose such a question if the answer was the obvious one. 

What will happen is either an ASSERT failure or an access fault. If you exercise a bit of debugging cleverness, you will find that the pointer to the main window is NULL! But how can this be! The application clearly has a main window...

Well, the answer is, yes, the application has a main window. But that's not what AfxGetMainWnd is defined as returning! Read the documentation carefully, and you will see that it says:

If AfxGetMainWnd is called from the application's primary thread, it returns the application's main window according to the above rules. If the function is called from a secondary thread in the application, the function returns the main window associated with the thread that made the call. [emphasis added]

A worker thread has no main window. Therefore, the call will return NULL. The workaround is to obtain a pointer to the application's main window by calling AfxGetMainWnd in the primary thread, and store it in a place (such as a member variable of the class) where the worker thread can find it.

Waiting for a thread to start  (27-Jan-01)

I recently processed a newsgroup message which was discussing the problem of waiting for thread startup. The proposed mechanism looked like this:

BOOL waiting; // flag (this is one bug right here!)
void CMyClass::OnStartThread()
   {
    waiting = TRUE;
    AfxBeginThread(myThread, something);
    while(waiting) /* spin */ ;   // horrible!
   }

UINT CMyClass::myThread(LPVOID whatever) // *** static *** member function
   {
    ... initialization sequence here
    waiting = FALSE; // allow initiatior to continue (probably won't work)
    ... thread computations
    return 0;
   }

This code has several problems. First, it is a terrible implementation; it requires that the parent thread run until its timeslice ends, which means that it delays the startup of the created thread. This alone is enough reason to throw the code out. But even if it were acceptable, it is still wrong. The waiting variable must be declared as volatile, because under the optimizing compiler, the while loop which spins may or may not be executed, and if it is executed, it will probably never exit. I discuss this in more detail in my essay on "Surviving the Release Build". But the bottom line is that when you have a variable of any sort that can be modified from one thread and whose modification must be detected in another, you must declare it as volatile.

The busy-wait is the serious disaster here. Since a timeslice, or quantum, is 200 ms in NT, it means that the thread can waste up to 200 ms, doing nothing, before allowing the spawned thread to run. If the spawned thread blocks on something like an I/O operation, and control returns to the creator, each time control returns to the creator, it will burn up another 200ms (the kernel doesn't care that it is doing nothing but polling a Boolean variable that can never change while it is polling it on a uniprocessor; it only knows that the thread is executing). As you can see, it doesn't take very many I/O operations in the thread to add many seconds to the perceived thread startup time.

The correct solution is shown below. In this solution, a manual-reset Event is used. To simplify the code, we create it just before it is needed, and destroy it immediately afterwards; in the case where the thread may be started up several times, the optimization to move this out to a member variable should be obvious. Note that doing this as a member variable suggests that the Event would be created in the class's constructor and destroyed in its destructor.

class CMyClass : public CView { // or something like this...
     protected:
         HANDLE startupEvent;
 };

void CMyClass::OnStartThread()
   {
     startupEvent = ::CreateEvent(NULL, // no security attributes
                                  TRUE, // manual-reset
                                  FALSE,// initially non-signaled
                                  NULL);// anonymous
     AfxBeginThread(myThread, this);
     switch(::WaitForSingleObject(startupEvent, MY_DELAY_TIME))
         { /* waitfor */
          case WAIT_TIMEOUT:
              ... report problem with thread startup
              break;
          case WAIT_OBJECT_0:
              ... possibly do something to note thread is running
              break;
         } /* waitfor */
     CloseHandle(startupEvent);
     startupEvent = NULL; // make nice with handle var for debugging
   } 

UINT CMyClass::myThread(LPVOID me)
   {
    CMyClass * self = (CMyClass *)me;
    self->run;
    return 0;
   }

void CMyClass::run( )
   {
    ... long initialization sequence
    ::SetEvent(staruptEvent);
    ... loop computations
   }

Note that I haven't done anything about dealing with the fact that the startup timeout may be incorrect and the thread is still trying to start; this could be handled by, for example, attempting to ::WaitForSingleObject on the thread handle with a wait time of 0; if it times out, the thread is running; if it returns with WAIT_OBJECT_0 the thread has halted. This requires that you deal with the issues of the CWinThread object possibly being deleted before you can get the handle. No, I'm not going to try to write every possible line of code.

Actually, it is rare that I would do something like this. I'd be more inclined to use messages posted to the main window to establish state for the GUI: the thread is starting, the thread has started, the thread has terminated (note that it may be terminated without being started). This avoids the issues about the GUI blocking until the thread has actually completed the startup sequence, or dealing with the timeout issue if the thread died somehow before doing the ::SetEvent.

void CMyClass::OnStartThread( )
   {
    AfxBeginThread(myThread, this);
    PostMessage(UWM_THREAD_STARTING);
   }

UINT CMyClass::myThread(LPVOID me) // *** static *** member function
   {
    CMyClass * self = (CMyClass *)me;
    self->run( );
    return 0;
   }

void CMyClass::run( )
   {
    ... lengthy startup sequence
    PostMessage(UWM_THREAD_STARTED);
    ... long thread computation
    PostMessage(UWM_THREAD_STOPPING);
    ... long thread shutdown
    PostMessage(UWM_THREAD_TERMINATED);
   }

I use the above paradigm in many contexts. Note that it completely eliminates the need for synchronization, but adds some complexity to the GUI. For example, imagine that I have in the GUI (in this case, since I'm posting to the view, it is view-specific state) a member variable that encodes the current state: terminated-or-never-started, starting, stopping, and running. I might have menu items called Start Computation, Pause Computation, Cancel Computation. I would create ON_UPDATE_COMMAND_UI handlers that responed as follows:

void CMyClass::OnUpdateStart(CCmdUI * pCmdUI)
   {
    pCmdUI->Enable(threadstate == MY_THREAD_STOPPED);
   }
void CMyClass::OnUpdatePause(CCmdUI * pCmdUI)
   {
    pCmdUI->Enable(threadstate == MY_THREAD_RUNNING);
   }
void CMyClass::OnUpdateCancel(CCmdUI * pCmdUI)
   {
    pCmdUI->Enable(threadstate == MY_THREAD_RUNNING);
   }

Providing I didn't really need to wait (and I find that I rarely do), I have now avoided the need to introduce a blocking synchronization event in the main GUI thread, which could potentially lock up the GUI. Note also that I might change the Cancel case to allow for the thread to be cancelled even if it is the middle of starting, providing that this makes sense in the context of the thread computation. In this case, I'd have to "poll" the cancel flag during the startup, for example, by splitting out the startup into a separate function:

void CMyClass::run( )
   {
    BOOL byCancel = FALSE;
    if(!startMe( ))
     { 
      PostMessage(UWM_THREAD_STOPPED, TRUE); // stopped by cancel
      return;
     }
    PostMessage(UWM_THREAD_STARTED);
    while(!cancelFlag)
      { /* thread loop */
    ...lengthy thread computation
      } /* thread loop */
    
    byCancel = cancelFlag;
    PostMessage(UWM_THREAD_STOPPING);
    ...lengthy thread shutdown
    PostMessage(UWM_THREAD_STOPPED, byCancel);
   }

BOOL CMyClass::startMe( )
   {
    ...do something
    if(cancelFlag)
      return FALSE;
    ...open the file on the server
    if(cancelFlag)
       {
        ...close the file on the server
        return FALSE;
       }
    ... more stuff, following above idea
    return TRUE;
   }

As usual in such cases, it is important to undo everything you have done if you detect the CancelFlag has been set during startup. I've also defined WPARAM of the message to include a flag that indicates if the thread stopped because it stopped normally or stopped because the user cancelled it (which I might use to display in the log that the thread was stopped by user request). I might also extend this to a set of enumerated types to communicate back error codes in case the thread decided to terminate because of some problem. I might even use LPARAM to hold the ::GetLastError code. You see, there are many themes-and-variations of this basic scheme.

Pausing a Thread and Thread Shutdown 

A thread may have to stop and wait for some reason. Perhaps the user has clicked a "pause" check box or pushed a "stop" button. Perhaps the thread has nothing to do, and is waiting for some information, such as request packet, to process. The problem is that you need to shut down all the threads before a process exits (note: in Windows CE, shutting down the main thread shuts down the process, and all threads owned by the process. This is not true in Win9x, Windows NT, or Windows 2000). A typical bug encountered is that you shut down your program, recompile it, and get an error that it is unable to write the executable file. Why? Because the program is still running. But you don't see it on the taskbar. So you bring up the Task Manager (via Ctrl-Alt-Del) and it isn't there, either. But something has got your executable tied up! The answer is that if you look in the NT Task manager under the processes tab, or use pview to look at processes, you will indeed find that your program is still running. This is usually because you failed to shut down one or more worker threads. As long as any one thread exists, even if it is blocked, your process is still alive. Of course, if you've killed the GUI thread, so the main window is gone and nothing is visible. But it is still lurking. Of course, you can use the NT Task Manager, or pview, to kill the process by terminating all of its threads, but this is a little bit like using dynamite to lift your car to change the tire. Sure, it lifts the car, but there are a few other things that go wrong which are considered undesirable side effects.

Three functions immediately present themselves for purposes of pausing or shutting down a thread: the SuspendThread and ResumeThread methods (and their underlying API calls, ::SuspendThread and ::ResumeThread) and ::TerminateThread. Assume, for all practical purposes, except in some very limited contexts, these functions do not exist. Using them will almost always get you in trouble.

The limited contexts in which these can be used are

  • A thread is generally free to call SuspendThread (or ::SuspendThread) on itself, providing it knows itself to be in a "safe" state (for example, it is not holding any synchronization primitive it is supposed to release). The risk here is that you don't know what your caller has locked, and you could be suspending a thread that the caller of your function was not expecting to be suspended. So be extremely careful even in this limited case!
  • A thread is always free to call ResumeThread (or ::ResumeThread) on another thread it knows is suspended. For example, in the case of creating/starting a thread in suspended mode, you have to call ResumeThread/::ResumeThread to get it to run at all!

Note that it is not a good idea to have a thread call TerminateThread on itself, because this will mean that it terminates instantly. The implication of this is that the DLL_THREAD_DETACH events for various DLLs will not be executed, which can lead to the misbehavior of a DLL you didn't even know you were using! (If you don't understand what this means, take it as meaning: bypassing DLL_THREAD_DETACH is a Very Bad Thing). Instead, if a thread wants to kill itself, it should call ExitThread, which guarantees the correct notification of all the DLLs.

Note that you should not substitute SuspendThread/ResumeThread for the proper use of synchronization objects such as Events and Mutexes.

To illustrate why it is a Bad Idea to let one thread suspend another, let's take a simple case: you have a worker thread off doing something, and the something involves memory allocation. You click the "Pause" button on your GUI, which immediately calls SuspendThread. What happens? The worker thread stops. Right now. Immediately. No matter what it is doing. If your worker thread happens to be in the storage allocator, you have just shut down your entire application. Well, not quite--it won't shut down until the next time you try to allocate memory from the GUI thread. But the MFC library is fairly liberal in using memory allocation, so there is an excellent chance that the next call to the MFC library from any thread will simply stop that thread dead in its tracks. If it is your GUI thread (the most likely one) your app appears to hang.

Why is this?

The storage allocator is designed to be thread-safe. This means that at most one thread at a time is permitted to be executing it. It is protected by a CRITICAL_SECTION, and each thread which attempts to enter the allocator blocks if there is already a thread in the allocator. So what happens if you do SuspendThread? The thread stops dead, in the middle of the allocator, with the critical section lock held. This lock will not be released until the thread resumes. Now, if it happens that your GUI requires an allocation as part of resuming the thread, an attempt to resume the thread will block, producing classic deadlock. And if you did a ::TerminateThread, then there is no way the lock will ever be released. And without SuspendThread, there is no need for ResumeThread

Ah, you say, but I know I don't do any memory allocation either in the worker thread or the GUI. So I don't need to worry about this!

You're wrong.

Remember that the MFC library does allocations you don't see. And allocation is only one of many critical sections that exist inside the MFC library to make it thread-safe. And stopping in any of them will be fatal to your app.

Ignore the existence of these functions.

So how do you suspend or terminate a thread?

The problems of shutting down a thread and pausing a thread are closely related, and my solution is the same in both cases: I use a synchronization primitive to effect the pause by suspending the thread, and use timeouts on the primitive to allow me to poll for shutdown.

Sometimes the synchronization primitive is a simple event, such as in the example below where I wish to be able to pause the thread. In other cases, particularly where I'm using the thread to service a queue of events, I will use a synchronization primitive such as a semaphore. You can also read my essay on GUI Thread Techniques.

Typically, I use a worker thread in the background, and it simply polls some state (such as a Boolean) to determine what it should be doing. For example, to pause a thread, it looks at the Boolean that says "Paused", which is set when (for example) a checkbox is set:

// thread body:
while(running)
   { /* loop */
    if(paused)
       switch(::WaitForSingleObject(event, time))
          {
           case WAIT_OBJECT_0:
              break;
           case WAIT_TIMEOUT:
              continue;
          }
     // rest of thread
    } /* loop */

The trick of doing the continue for the timeout means that the thread will regularly poll for the running flag being clear, simplifying your shutdown of the application. I typically use 1000 for the time, polling once a second for shutdown. 

Why did I do the apparently redundant test of the paused Boolean variable before doing the ::WaitForSingleObject? Wouldn't waiting on the object be sufficient?

Yes, the paused Boolean is an optimization hack. Because I use ::WaitForSingleObject, each pass through this loop involves a moderately heavy-duty operation to test for continuance. In a high-performance loop this would introduce a completely unacceptable performance bottleneck. By using a simple Boolean I can avoid the heavy-duty kernel call when I don't need it. If performance is not an issue, you can eliminate this extra test.

The code in the main GUI thread that sets these variables is as shown below:

CMyDialog::OnPause()
   {
    if(paused && c_Pause.GetCheck() != BST_CHECKED)
       { /* resume */
        paused = FALSE;
        SetEvent(event);
       } /* resume */
    else
    if(!paused && c_Pause.GetCheck() == BST_CHECKED)
       { /* pause */
        paused = TRUE;
        ResetEvent(event);
       } /* pause */
   }

where event is a handle from ::CreateEvent. Avoid CEvent--at least the last time I tried it, it was so unbelievably brain-dead buggy that it was unusable. I haven't checked the VC++ 6.0 implementation, so it may have been fixed, but the bottom line is that the MFC interface to synchronization primitives has been deeply suspect, and gains nothing over using the actual primitives.

There's another, slightly more complex, mechanism for doing a thread shutdown without polling, which I discuss in a later section.

Shutting down a thread from a view or main frame  

There is sometimes a problem in shutting down a thread. If you don't do things in the right order, you could even shut down your GUI thread while the worker thread is still running, which can lead to all sorts of interesting problems. Interesting, as in the traditional Chinese curse. So here's a method I've used to shut down a thread a be sure it is shut down before the view is shut down.

First, you must store a pointer to the CWinThread object in your view, so declare

CWinThread * myWorkerThread;

in your view. When you create the worker thread, create it as

myWorkerThread = AfxBeginThread(run, this);

You will need this variable to synchronize the shutdown with the view termination.

void CMyView::OnClose()
    {
     // ... determine if we want to shut down
     // ... for example, is the document modified?
     // ... if we don't want to shut down the view, 
     // ... just return
  
     // If we get here, are are closing the view
     myWorkerThread->m_bAutoDelete = FALSE;
     running = FALSE;
     WaitForSingleObject(myWorkerThread->m_hThread, INFINITE);
     delete myWorkerThread;
     CView::OnClose(); // or whatever the superclass is
    }

The only odd thing that appears in the previous function is the saving of the m_bAuthoDelete flag explicitly to FALSE. This is because the deletion of the CWinThread-derived object can close the handle of the thread, rendering the subsequent WaitForSingleObject invalid. By inhibiting the auto-deletion, we can wait on the thread handle. We then do the explicit deletion of the CWinThread-derived object ourselves, since it is now no longer useful.

Special thanks to Charles Doucette for pointing out a flaw in my original article which he found in another essay by Doug Harrison: there was a race condition; I had previously stored the handle and shut down the thread. But the auto-delete invalidated the handle which could lead to incorrect behavior.  

By storing the handle to a variable, we can then do a WaitForSingleObject on the thread. The close operation then blocks until the thread terminates. Once the thread has terminated, we can proceed with the close by calling our superclass OnClose handler (in this example, we are a derived class of CView).

There is a caution here: this assumes that the thread will actually terminate "within a reasonable time". If you have a thread that is blocked on I/O or a synchronization object, you will need to add a timeout mechanism as I have already described. Note that this prohibits the use of CRITICAL_SECTION as a synchronization object since they don't have a timeout capability. If you're blocked on a CRITICAL_SECTION you're stuck forever.

Of course, in the general case you may have several synchronization mechanisms that are necessary to ensure the thread will terminate within a reasonable period of time. A serious design flaw in the whole AfxBeginThread mechanism is that it doesn't allow me to create a CWinThread-derived subclass of my own which is the object created. In this case, I've sometimes subclassed CWinThread and bypassed the AfxBeginThread by doing my own thread creation inside my subclass, and exporting methods such as CMyWinThread::shutdown that do whatever is needed to make the thread shut down cleanly and quickly.

Thread Shutdown Without Polling  (20-Jan-01)

The technique I use that polls every few seconds does have two implications: it makes the thread active every few seconds, and it sets a limit on responsiveness on a shutdown. The shutdown of the application becomes limited by the maximum time it takes to get out of the thread-wait operation. There is an alternative implementation I have also used, which involves using a second Event. 

HANDLE ShutdownEvent;

This should be initialized via CreateEvent. What I do when I'm using this technique is include it in a class derived from CWinThread, which makes the thread creation slightly trickier. This is because AfxBeginThread always creates a new CWinThread object, but if you need your own CWinThread-derived class, you can't use AfxBeginThread. The technique shown below generalizes this. Note that if I wanted to be really general, I would create a template class. I leave that as an Exercise For The Reader.

/***********************************************************************
*                                class CMyThread
***********************************************************************/
class CMyThread : public CWinThread {
     public:
        CMyThread( );
        virtual ~CMyThread( );
        static CMyThread * BeginThread(LPVOID p);
        void Shutdown( );
        enum { Error, Running, Shutdown, Timeout };
     protected: // data
        HANDLE ShutdownEvent;
        HANDLE PauseEvent;
};
/**********************************************************************
*                        CMyThread::CMyThread
* Inputs:
*        AFX_THREADPROC proc: Function to be called
*        LPVOID p: Parameter passed to proc
***********************************************************************/
CMyThread::CMyThread(AFX_THREADPROC proc, LPVOID p ) : CWinThread(proc, p)
   {
     m_bAutoDelete = FALSE;
     ShutdownEvent = ::CreateEvent(NULL,   // security
                                   TRUE,   // manual-reset
                                   FALSE,  // not signaled
                                   NULL);  // anonymous

     PauseEvent = ::CreateEvent(NULL,      // security
                                TRUE,      // manual-reset
                                TRUE,      // signaled
                                NULL);     // anonymouse
   }

/**********************************************************************
*                         CMyThread::~CMyThread
**********************************************************************/
CMyThread::~CMyThread( )
   {
    ::CloseHandle(ShutDownEvent);
    ::CloseHandle(PauseEvent);
   }

/*********************************************************************
*                        CMyThread::BeginThread
* Result: CMyThread *
*        Newly-created CMyThread object
*********************************************************************/
CMyThread * CMyThread::BeginThread(AFX_THREADPROC proc, LPVOID p)
   {
    CMyThread * thread = new CMyThread(proc, p);
    if(!thread->CreateThread( ))
        { /* failed */
         delete thread;
         return NULL;
        } /* failed */
    return thread;
   }
/*********************************************************************
*                         CMyThread::Wait
* Result: DWORD
*       WAIT_OBJECT_0 if shutting down
*       WAIT_OBJECT_0+1 if not paused
* Notes:
*       The shutdown *must* be the 0th element, since the normal
*       return from an unpaused event will be the lowest value OTHER
*       than the shutdown index
*********************************************************************/
DWORD CMyThread::Wait( )
   {
    HANDLE objects[2];
    objects[0] = ShutdownEvent;
    objects[1] = PauseEvent;
    DWORD result = ::WaitForMultipleObjects(2, objects, FALSE, INFINITE);
    switch(result)
      { /* result */
       case WAIT_TIMEOUT:
           return Timeout;
       case WAIT_OBJECT_0:
           return Shutdown;
       case WAIT_OBJECT_0 + 1:
           return Running;
       default:
           ASSERT(FALSE); // unknown error
           return Error;
      } /* result */
   }

/********************************************************************
*                        CMyThread::Shutdown
* Effect:
*        Sets the shutdown event, then waits for the thread to shut
*        down
********************************************************************/
void CMyThread::Shutdown( )
   {
    SetEvent(ShutdownEvent);
    ::WaitForSingleObject(m_hThread, INFINITE);
   }

Note that I don't make provision here for the full set of options for CreateThread, since the threads I create do not need flags, stack size, or security attributes; you would need to make the obvious extensions if you need these features.

To call it from an application, I do something like the following. In the declaration of the class in which the thread will run, such as a view class, I add declarations like

CMyThread * thread; // worker thread
static UINT MyComputation(LPVOID me);
void ComputeLikeCrazy( );

Then I add methods, such as this one that responds to a menu item or pushbutton in a view:

void CMyView::OnComputationRequest( )
   {
    thread = <A href="#CMyThread::BeginThread">CMyThread::BeginThread</A>(MyComputation, this);
   }

UINT CMyView::MyComputation(LPVOID me) // static method!
   {
    CMyView * self = (CMyView *)me;
    self-><A href="#CMyView::ComputeLikeCrazy">ComputeLikeCrazy</A>( );
   }

The code below then shows how I implement a "pause" capability. Alternatively, the PauseEvent variable could represent a Semaphore on a queue or some other synchronization mechanism. Note, however, that it is more complex if you want to wait for a semaphore, a shutdown, or a pause. In this case, because you can only wait on "or" or "and" of the events, and not more complex relationships, you will probably need to nest two WaitForMultipleObjects, one for the semaphore-or-shutdown combination and one for the pause-or-shutdown combination. Although I don't show it below, you can additionally combine this technique with a timeout. Note that in the example below, the running flag is actually local, rather than being a class member variable, and is implicitly handled by the case decoding the ShutdownEvent.

void <A name=CMyView::ComputeLikeCrazy>CMyView::ComputeLikeCrazy</A>( )
   {
    BOOL running = TRUE;

    while(running)
      { /* loop */
       DWORD result = thread-><A href="#CMyThread::Wait">Wait</A>( );
       switch(result)
          { /* result */
           case <A href="#enum">CMyThread::Timeout</A>:   // if you want a timeout...
              continue;
           case <A href="#enum">CMyThread::Shutdown</A>:  // shutdown event
              running = FALSE;
              continue;
           case <A href="#enum">CMyThread::Running</A>:   // unpaused
              break;
          } /* result */
       // ...
       // ... compute one step here
       // ...
      } /* loop */
   }

Note that I make provision for a timeout case, even though the current implementation does not provide for it (an Exercise For The Reader).

Synchronization

Any time you have state shared between two threads it is essential that you provide synchronization on accesses. I discuss a fair amount of this in our book, Win32 Programming, and don't plan to replicate that discussion here. What is odd, however, is the fact that for variables such as paused I don't provide any synchronization. How can I get away with this?

The answer is that synchronization is not required providing only one thread ever modifies the variable, at least in some restricted cases. In our examples, the main GUI thread modifies the variable paused, but all other threads, such as the worker thread, only read it. It is true that the worker thread might, by a single instruction, miss detecting it, but the idea here is that one additional loop of the worker thread won't matter anyway, because the user might have missed it by tens or hundreds of milliseconds.

It has been pointed out to me that even if only one thread modifies the variable (although several threads may use it), if it takes more than one instruction (or one memory cycle) to do it, synchronization is required. For example, if the value is a 64-bit value and two 32-bit instructions are used to store it (because you did not compile for a native Pentium instruction set), you  could have the modifying thread preempted after it has stored the first 32 bits (whichever, high or low, the compiler has chosen to do first) but not the second 32 bits. This is, in fact, correct. If you are modifying a scalar value of more than 32 bits, and the generated code requires more than two instructions to store the value, you must still do synchronization between the modifier and users of the value to ensure that you have not been victimized by this anomaly. Note that if the compiler generates a 64-bit store, there might not a be a problem. The Pentium bus is 64 bits wide, and synchronization is done at the hardware level. But if the value is not aligned so that a single memory cycle can store it (for example, a 32-bit value split across a 64-bit boundary), two memory cycles are required to complete the store, making it risky for a multiprocessor environment. Therefore, you should be careful about taking advantage of this feature. A Tip of the Flounder Fin to Chris Bond for pointing this out.

So what about that case where I set running to be TRUE before AfxBeginThread and set it FALSE just as the thread exited? That just violated my previous statement, didn't it? Well, yes, But note that in this case the synchronization still exists. The thread is terminating, and therefore any computation left to be done in the thread is about to complete. No other work will be done in the thread. The GUI thread will not start a new thread until the running flag is FALSE. Unless you've set the m_bAutoDelete member of the CWinThread to be FALSE, all cleanup including deleting the object will be handled automatically. So we can actually "get away with it". 

If you want to be totally correct and precise, the only valid solution is to have yet another thread waiting on your first worker thread, and when the first worker thread completes, the second worker thread starts up, sets running to FALSE, and then terminates itself. This is a little clumsy and is essentially overkill, but is formally correct.

Summary

Working with threads introduces some complications, but compared to the problems of dealing with PeekMessage is a much better generalization of the notion of parallel computation. The amount of care that has to be exercised is startling at first, but after you've done a few multithreaded applications it becomes almost a reflex. Learn to use threads. You will be better off in the long run.

History

(27-Jan-01) In response to several questions about ResumeThread, I've expanded my discussion of this topic.

  (27-Jan-01) I've added, in response to some issues in the newsgroup, a discussion of how to wait for a thread to start up.

  (20-Jan-01) An alternative mechanism for doing thread shutdown--specifically, how you detect within the thread it is being shut down--is now documented (the method I wished I'd thought of when I wrote the chapter in Win32 Programming, and figured out about six months after it went to press). I had meant to put this in the original essay and forgot about it.

  (10-Apr-00) A flaw in the thread shutdown logic was pointed out to me; you have to inhibit auto-deletion of the CWinThread-derived object explicitly!

  A new section on shutting down view-related threads has been added.

  (28-Jan-00) The description of pausing and resuming threads has been enhanced with a more detailed discussion of why SuspendThread and ResumeThread must be avoided.


The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

Send mail to newcomer@flounder.com with questions or comments about this article.
Copyright © 1999 CompanyLongName All Rights Reserved.
www.flounder.com/mvp_tips.htm

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Joseph M. Newcomer
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMessage Box and Do Modalmemberapocalyptic.angell4 May '11 - 14:10 
Thanks so much for this article. It's been very helpful in understanding worker threads and what they do. I do however have a question. I was wondering if you could explain a little more in depth why one shouldn't call Message Box and DoModal from a worker thread. I built a small test application that uses worker threads and the thread calls MessageBox and seems to work just fine. Also, I understand that the worker thread itself does not have a message pump, but I thought that MessageBox and DoModal created their own message pumps separate from the existing message pumps. I've included my worker thread code below so maybe you can tell me why it is working when it shouldn't...
 
CTheads1dlg.h
class CTheads1Dlg : public CDialog
{
// Construction
...
 
// Dialog Data
...
 
// Implementation
protected:
	void Sleeper(int secs);
	static UINT SleeperThread(LPVOID p);
	HICON m_hIcon;
	volatile bool threadRunning;
 
...
}
 
CThead1dlg.cpp
void CTheads1Dlg::OnStartThread() //Called when user clicks Start Thread button 
{
	// TODO: Add your control notification handler code here
	if(!threadRunning)
	{
		threadRunning = true;
		AfxBeginThread(SleeperThread, this);
	}
}
 

UINT CTheads1Dlg::SleeperThread(LPVOID p)
{
	CTheads1Dlg * me = (CTheads1Dlg*) p;
	me->Sleeper(10);
	return 0;
}
 
void CTheads1Dlg::Sleeper(int secs)
{
	DWORD period = secs*100;
 
	for(int i(0); i<10; i++)
	{
		MessageBox("Hey I'm a message box!");
		Sleep(period);
	}
        threadRunning = false;
}

QuestionHow do you pause threads?memberpinload13 Feb '11 - 23:55 
First of all i have to say "thanks" for your nice work and according to my beginning multithreading exercise i can not understand your Event Handler. Because you start the PauseEvent signaled. Maybe somebody wants the active PauseEvent running the thread and not signaled TIME_OUT. But Under these conditions your Pause() Methode will set the event signaled although the state is already signaled. Makeing the flag unsignaled with ResetEvent ends in an INFINITE-Loop caused by WaitForMultipleObjects-Flag set to FALSE. If this is the proper way could you explain how do you pause threads?
 

CMyThread::CMyThread(AFX_THREADPROC proc, LPVOID p ) : CWinThread(proc, p)
{
     /.../
     PauseEvent = ::CreateEvent(NULL,      // security
                                TRUE,      // manual-reset
                                TRUE,      // signaled
                                NULL);     // anonymouse
}
     /.../
 
DWORD CMyThread::Wait( )
{
    HANDLE objects[2];
    objects[0] = ShutdownEvent;
    objects[1] = PauseEvent;
    DWORD result = ::WaitForMultipleObjects(2, objects, FALSE, INFINITE);
    switch(result)
      {case WAIT_TIMEOUT:
           return Timeout;
       case WAIT_OBJECT_0:
           return Shutdown;
       case WAIT_OBJECT_0 + 1:
           return Running;
     /.../
void CMyThread::Pause( )
{
    SetEvent(PauseEvent);
    ::WaitForSingleObject(m_hThread, INFINITE);
}

Generalhelp on worker threadmemberashwani_gupt11 Jan '11 - 1:03 
hi mr newcomer or anykind soul,
 
the example in this article has the thread as a fuction within myclass.following the same i created the following.
 
void CScada_thrdsView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
SetTimer(1,1000,0);
m_thrd = AfxBeginThread(wrkr_thrd,this);
count = 0;
}
 

 
UINT CScada_thrdsView::wrkr_thrd(LPVOID p)
{CScada_thrdsView* pvw = (CScada_thrdsView*)p;
while (!exit_thrd)
{while(!nw_cnt) ;
pvw->dsp_cnt();
nw_cnt = 0;
}
return (1);
 
}
 
void CScada_thrdsView::dsp_cnt()
{
sprintf(cstring,"%d",count);
m_val.InsertString((count-1),cstring);
UpdateData(FALSE);
}
 
but i get a compilation error as
C:\Program Files\Microsoft Visual Studio\MyProjects\scada_thrds\scada_thrdsView.cpp(74) : error C2665: 'AfxBeginThread' : none of the 2 overloads can convert parameter 1 from type 'unsigned int (void *)'
for the AfxBeginThread statement.
 
please help.
 
ashwani
QuestionIs possible, from worker thread, to start a function in UI thread to it run in UI Thread context ?memberSNArruda3 Apr '09 - 12:10 
Hi,
 
I have a project exactly igual your example. I Started a worker thread to manage text files created by UI Thread by execution menu itens. The files created containing informations such as User, date, time, function in UI Thread that realy do the task. The file is puted into a directory for Worker thread manager. It means that worker thread is responsible for open a file (Or files one by one) and locate de parameter that has a function in UI Thread to be executed and start that function directly in UI Thread function.
 
Is it possible ?
 
There are a way to directly start a function in UI Thread context, from worker thread context ?
 
My intention is Worker thread to run as a person who call every functions in UI Thread.
 
I would´t like to usu notifications to UI Thread. Will be the must if i will get call by a pointer.
 
Help me ... please ... i need it very much ...
AnswerRe: Is possible, from worker thread, to start a function in UI thread to it run in UI Thread context ?memberJoseph M. Newcomer3 Apr '09 - 13:43 
OK, I'm having a little trouble making sense of the question, but I think what you are asking is:
 
Can I invoke a function that is running in the UI thread from a worker thread?
 
Yes. Note that this function, once invoked, runs in the context of the UI thread; that is, if it takes 60 seconds to run, the user interface (menus, etc.) is completely nonresponsive for 60 seconds. THe most common reason to invoke a function in the main GUI thread is to perform operations only the main GUI thread can perform; for exmaple, updating a window.
 
For low-bandwidth communication, the PostMessage call from the secondary thread to post a message to the main GUI thread accomplishes this. So you would create a user-defined message handler and create an entry for it. See my essay on message management.
 
I use Registered Window Messages, so my code looks like this:
 
static UINT UWM_DO_SOMETHING = ::RegisterWindowMessage(_T("UWM_DO_SOMETHING") _T(""));
 
ON_REGISTERED_MESSAGE(UWM_DO_SOMETHING, OnDoSomething)
 
LRESULT CMyClass::OnDoSomething(WPARAM wParam, LPARAM)
{
SomethingParametersBlock * b = (SomethingParametersBlock *)wParam;
MyFunction(b);
delete b;
return 0;
}
 
To invoke the functionality of MyFunction, to be executed in the main GUI thread, in the secondary thread you would write
 
SomethingParametersBlock * b = new SomethingParametersBlock;
b->... = ...; // set values as appropriate
wnd->PostMessage(UWM_DO_SOMETHING, (WPARAM)b);
 
wnd is a CWnd* parameter you pass into the thread.
 
But note that you need the message handler to do this. I'm not sure what you mean by "every function", because there are hundreds of functions that wouldn't make sense to invoke.
 
If you are looking at creating some kind of scripting language, life gets more confusing, because you can't let the user do anything while the script is running except stop the script, so you have to disable all user interaction that can be destructive. Note this includes pretty much everything.

GeneralWorker Thread [modified]memberbitez!31 Mar '08 - 14:57 
Hi ya!
 
First of all, I'd like to say thank you for such a great article.
But even so, I'm really a noob with any threads, and synchronization. Thus, right now, I'm very 'unshameful' of asking for your guidance. Smile | :)
 
I'm currently working on a simple installer project which uses UNRAR.DLL, to unpack the installation files. It's MFC dialog based, and I'm compiling with Microsoft VC++ 6.0.
 
This is what I did (before reading your article):
 
// members definition under the CProgressDlg
...
int nErrorCode;
CString szDestinationPath;
CString szFileName;
...
void Install();
 
// global variables
...
CStringList szFiles;
CUnRar unrar;
...
 
// declaration of the members under the CProgressDlg
void CProgressDlg::Install()
{
    POSITION pos = szFiles.GetHeadPosition();
 
    m_ctrlProgressBar.SetRange(0, szFiles.GetCount());
 
    for (int i = 0; i < szFiles.GetCount(); i++)
    {
      szFileName = szFiles.GetNext(pos);
      nErrorCode = unrar.Extract(szDestination, szFileName);
      if (!nErrorCode)
        m_ctrlProgressBar.SetPos(i);
      else
        break;
    }
}

 
Everything went well with the implementation above, except for the very BAD thing you mentioned in the article; A GUI deadlock. The GUI thread no longer receive messages, and if I switch between windows, well... it gets ugly for not processing the WM_PAINT until the file extraction process is finished.
 
This is what I did (after reading your article):
 
// added members to the CProgressDlg
...
static UINT Run(LPVOID p);
void Run();
volatile bool bRunning;
...
 
void BeginWorkerThread();
void StopWorkerThread();
 
// declaration of the new members
void CProressDlg::BeginWorkerThread()
{
    bRunning = true;
    AfxBeginThread(Run, this);
}
 
void CProgressDlg::StopWorkerThread()
{
  bRunning = false;
}
 
UINT CProgressDlg::Run(LPVOID p)
{
    CProgressDlg *me = (CProgressDlg *)p
    me->Run();
    return 0;
}
 
void CProgressDlg::Run()
{
    if (bRunning)
    {
      POSITION pos = szFiles.GetHeadPosition();
      for (int i = 0; i < szFiles.GetCount(); i++)
      {
        szFileName = szFiles.GetNext(pos);
        nErrorCode = unrar.Extract(szDestination, szFileName);
        if (nErrorCode)
        {
          PostMessage(ERROR_EXTRACT_MSG);
          break;
        }
        else
          PostMessage(NOERROR_EXTRACT_MSG);
      }
      StopWorkerThread();
    }
    else
      Sleep(100);
}
 
// revised old member
void CProgressDlg::Install()
{
    m_ctrlProgressBar.SetRange(0, szFiles.GetCount());
    BeginWorkerThread();
}

 
Okay, so basically the thread wasn't able to do its job. No files were extracted, and my application crashed... Frown | :(
 
I think I might have did it the lame way.
 
modified on Monday, March 31, 2008 9:10 PM

GeneralRe: Worker ThreadmemberJoseph M. Newcomer31 Mar '08 - 16:34 
Define "crash", which is a completely meaningless term unless accompanied by a precise description.
 
What is the purpose of the Sleep(100) call? This seems to be a complete waste of effort; it serves no detectable purpose I can see.
 
Why do you have some complex mechanism like StopWorkerThread() which is called from within the thread? Study the logic! This code does nothing at all that is meaningful! If you finish with all the files, the thread exits normally, and the StopWorkerThread does nothing!
 
In fact, why is there a test for if(bRunning) at all? This serves no purpose whatsoever. It is not tested in the loop, where you might want to test it to see if the user is aborting the installation, and therefore it doesn't do anything at all.
 
There is nothing here to aid in the diagnosis of the problem, which is unspecified. You have not shown any of the code involved in starting the thread, or what is done after it is started; it is entirely possible your app is exiting before the thread finishes. There's no way, particularly because of the use of the completely empty phrase "crash" to describe what happens to guess what has gone wrong.
GeneralRe: Worker Threadmemberbitez!31 Mar '08 - 19:52 
Wow! That was a very fast reply. Thank you! Smile | :)
Will fix most of the code now based on the points you stressed out.
 
Regards,
Brian
GeneralRe: Worker Threadmemberbitez!1 Apr '08 - 0:02 
Just to let you know that my worker thread is now working correctly.
It was really my fault. I should have read your article well and cross checked with my code before posting the question. The errors I committed were so obvious, and you already mentioned something similar in the article. So thank you for your effort to read my question and corrected me.
 
Regards,
Brian
QuestionWhy x defines two times?memberBigFoot_x218 Dec '07 - 0:39 
Why this?
for(int x=y = 0; y < image.height; y++)
Due to:
for(int x = 0; x < image.width; x++)
GeneralWhat's the difference? _threadstartex vs. Win32 ThreadmemberHamed Mosavi5 Jul '07 - 10:45 
When I shutdown my threads, in debug mode, I sometimes see the message:
 
The thread '_threadstartex' (0xa30) has exited with code 0 (0x0).
 
and some times:
The thread 'Win32 Thread' (0xc78) has exited with code 0 (0x0).
 
Are these two different?
 
// "Life is very short and is very fragile also." Yanni
while (I'm_alive)
{
cout<<"I love programming.";
}

GeneralRe: What's the difference? _threadstartex vs. Win32 ThreadmemberThatsAlok26 Jul '07 - 23:31 

Hamed Mosavi wrote:
The thread '_threadstartex' (0xa30) has exited with code 0 (0x0).

 
Thread Routine provide you by C RunTime Library
 

Hamed Mosavi wrote:
The thread 'Win32 Thread' (0xc78) has exited with code 0 (0x0).

 
Thread routine provide you By Windows Library!
 

"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David Crow


cheers,
Alok Gupta
VC Forum Q&A :- I/ IV
Support CRY- Child Relief

GeneralRe: What's the difference? _threadstartex vs. Win32 ThreadmemberHamed Mosavi27 Jul '07 - 1:13 
Really interesting, thank you.
 
ThatsAlok wrote:
Thread Routine provide you by C RunTime Library

 

ThatsAlok wrote:
Thread routine provide you By Windows Library!

 
But who controls the providerConfused | :confused: ? Why sometimes this one and sometimes that one? Is there any special rule or reason? The code to run the thread is the same.
 
Thank you again ThatsAlok. That was nice.
 
// "Life is very short and is very fragile also." Yanni
while (I'm_alive)
{
cout<<"I love programming.";
}

GeneralRe: What's the difference? _threadstartex vs. Win32 Threadmembermctenner29 Jul '07 - 9:19 
Threads created by the CRT function _threadstartex get the name "_threadstartex", whereas threads created by the Win32 API get the other name.
 
You can set a custom name to ease debugging if you've got a lot of threads: http://msdn2.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx
GeneralRe: What's the difference? _threadstartex vs. Win32 ThreadmemberHamed Mosavi29 Jul '07 - 9:48 
Thanks, mctenner.
 
The link and information was really helpful. I think I'll need it one day, for sure.
 
What I'm wondering here is that I have a code that runs a thread. Upon clicking on a button, another instance of the same thread runs, just like the first one. But when exiting, each one shows a different name as mentioned. Why does it happen? Does it mean that MFC decides on which version of the two provider should be used? But how and why?
 
// "Life is very short and is very fragile also." Yanni
while (I'm_alive)
{
cout<<"I love programming.";
}

GeneralRe: What's the difference? _threadstartex vs. Win32 Threadmemberemilio_grv21 Jan '10 - 20:23 
That's just a label.
When a thread is created by ::CreateThread it gets "win32 thread" by the Windows API itstelf.
 
When MFC creates a thread (can do it in a number of ways, there are more constructors and functions) it calls CreateThread, then changes that default label to something else before returning.
 
Probably one of the functions MFC uses to create threads miss such label replacement.
 
Or, may be the thread execution was so short that it terminates before the creator had the time to change the lablel.
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralWonderful!memberHamed Mosavi4 Jul '07 - 9:48 
Super great article. A really superb job.
 
I wish there was some more about thread synchronization, however I found one of the other articles you've written about semaphores. I hope I find answer to my remaining questions there.
 
Anyway this one certainly got my 5.
I hope to see more articles from you. Thanks for sharing.Rose | [Rose]
 
// "Life is very short and is very fragile also." Yanni
while (I'm_alive)
{
cout<<"I love programming.";
}

GeneralRe: Wonderful!memberJoseph M. Newcomer4 Jul '07 - 15:39 
My latest threading essay is
http://www.flounder.com/no_synchronization.htm
GeneralRe: Wonderful!memberHamed Mosavi4 Jul '07 - 19:48 
Thank you so much. I just started reading!
 
It was really kind of you to inform me about your article, and I can't find the word to express my thanks. Really. That made me so happy.
 
Thank you very much indeed.
 
// "Life is very short and is very fragile also." Yanni
while (I'm_alive)
{
cout<<"I love programming.";
}

GeneralRe: Wonderful!memberJoseph M. Newcomer5 Jul '07 - 7:38 
Thank you for the enthusiasm
GeneralRe: Wonderful!memberHamed Mosavi5 Jul '07 - 9:50 
Joseph M. Newcomer wrote:
Thank you for the enthusiasm

 
It's my pleasure Smile | :)
 
Mr. Newcomer,
You just can't believe how much help you provide with such articles, specially to beginners like me.
 
Almost a year ago I wrote a TAPI answering machine app. One of the main problems of that app, that still exists even here in an article in CP, was(and is) threads. Learning to start, stop and manage them was a nightmare, just because I was not aware of different scenarios that these(thread related) set of tools created for. Wish I had seen your articles in those days.
 
Articles like yours are like a brilliant treasure to me, so invaluable to be priced or as people say in English: Too good to be true.
 
I can never thank you enough.
 
// "Life is very short and is very fragile also." Yanni
while (I'm_alive)
{
cout<<"I love programming.";
}

QuestionQuestion regarding the poll and shutdownmembertom groezer29 Apr '07 - 14:03 
I would like to know the usecase for the worker thread below
 
if(!paused && c_Pause.GetCheck() == BST_CHECKED)
{ /* pause */
paused = TRUE;
ResetEvent(event);
} /* pause */

 
I guess the above means that the worker thread is paused but then why is the event reset. Doing so shall make the user thread wait on the event ? Now the question is how does it prevent from the undesirable effects of TerminateThread() and SuspendThread?

AnswerRe: Question regarding the poll and shutdownmemberJoseph M. Newcomer29 Apr '07 - 20:38 
No, it does not mean the worker thread is paused; it means that the main thread is under the impression it has not yet suggested to the worker thread that it should pause, and if it had not, it now claims that it has, and suggests to the worker thread that it pause by calling ResetEvent. The next time the worker thread hits a ::WaitForSingleObject on that event, it will block. No suggestion is made that the main GUI thread would wait on the event. The point here is that the worker thread waits on TWO events, the pause-event and a shutdown-event.
 
It does not prevent undersirable side effects of TerminateThread or SuspendThread; the way to prevent those undesirable side effects is to not call those functions.
 
Note that the fragment you quoted is out of context, and has to be read in the context of the OnPause handler, which deals with setting the event based on the check state of the control. Note its complementary component.
GeneralSynchronizationmemberAbhitha22 Apr '07 - 23:06 
I have 3 threads running ,in between i have to continuously monitor a input port for reading a data.My processor is not a multiprocessor.How I can monitor it.
 
abiiiiiiiii

GeneralRe: SynchronizationmemberJoseph M. Newcomer23 Apr '07 - 5:03 
You have the wrong idea about the problem.
 
There is no "in between". What you have is a specification that says every so often you need to monitor an input port (which, I might add, is a remarkably poor design for a device, and represents one of the worst possible ways to build a device). So you write a thread that monitors the port, and sleeps waiting for input.
 
Ideally, you would have an intelligently-designed device which did not require polling. Have you considered using a device that has such a design?
QuestionMemory leaked?memberkenjirotanaka10 Apr '07 - 21:36 
I think there are memory leaks in the codes.
I watched VC debugger detected memory leaks when the code ran below.
OnClose() is called.
The loop flag(running)is set in OnClose().
AddNameToControl() is called and post the message(UWM_ADDSTRING).
The loop in Run() is finished and the thread func is terminated before OnAddString() is called.
In the end, "delete s" statement in OnAddString() is not run.

QuestionASSERT causes deadlock?memberyph2004010730 Mar '07 - 20:55 
I have a GUI main thread. It starts a worker thread, but doesn't wait it to complete. So the main thread continues.
 
During the worker thread's execution, it generates a MFC ASSERT failure dialog. At the moment a deadlock occurs!!!
 
Why? How to solve this problem?
Thanks for your helps!!!

AnswerRe: ASSERT causes deadlock?memberJoseph M. Newcomer31 Mar '07 - 15:04 
This isn't a very good description.
 
It is typical to not wait for worker threads.
 
If an ASSERT occurs, it is indicative of some serious problem. Without useful information, such as what file it occurred on, what line it occurred on, and what version of VS you are using, no diagnosis is possible.
 
Why do you think there is "deadlock"? Deadlock is a very specific condition dealing with multiple threads blocked on multiple locks, so are you sure this is what has happened? If this is what happened, it means that you have a timing situation where the ASSERT has upset the timing enough to generate a deadlock, which means your locking strategy is defective in design and will have to be changed so that a canonical locking order is maintained. If you are seeing some other kind of hangup, it is important to know what it is. Nothing can be done with a description as vague as the one you have given.
QuestionRe: ASSERT causes deadlock?memberyph200401071 Apr '07 - 2:03 
I'm sorry for not having given you enough information. Details: When the ASSERT dialog pops up from the worker thread, and keeps open, after a while the application will freeze(but CPU used a little). I don't know why this happen. According to your article, dialog boxes should not be lanuched from a worker thread(but ASSERT dialog box must exist in any code blocks executed through any threads with debug version), so I guess it is a deadlock. Perhaps it may not be right. Will you tell me what kind of hangup may happen and what should be done?
 
Thank you again!!!

QuestionGUI should not be active, but need to show snake-like-progressbar ?memberana_v12327 Mar '07 - 3:34 
Hi
Thanks a lot for this article, It's extremely useful for me.
 
I need some guidance.
 
- We have a multi-tier multi-user application for business transactions (orders, sales invpoices, purchase transactions, payment, receipts, inventory etc.)
- Client (GUI) is developed with VC++6 & MFC
- Application Server is developped with VC++6, ATL/DCOM.
- Application Server has database connection pool with limited Number of Database Connections.
 
When any user (client app) requests Customers List, Vendors List etc., GUI will be waiting. We do not keep Client GUI active (because most user actions will generate request to server, which will need database, which is already not available).
When Application Server retrieves data, it sends required information. And GUI will become active.
 
I need to show snake-like-progressbar in StatusBar.
So I used worker thread for requesting data from server
and I used timer to show snake-like-progressbar in StatusBar.
 
It works. When server is busy (or database connection is not available), It shows snake-like-progressbar in StatusBar properly. But GUI (menus, toolbar buttons etc.) remains active. I do not want to keep Client GUI active (so user can not generate another request for Server). How to achieve that ?
 
Please guide me.
 
Thanks
Ana
AnswerRe: GUI should not be active, but need to show snake-like-progressbar ?memberJoseph M. Newcomer27 Mar '07 - 5:41 
Generally you want parts of the GUI to remain active. But not all parts. For example, you might want the File|Exit to remain active. The concern here is what to do if the database hangs indefinitely. There may be no reason to disable Help, About, or the ability in MDI to switch to another document.
 
Is this SDI, MDI or dialog-based?
 
Tpically what you do is set a flag that says "Query active" when you hand the request off to the thread. When the thread finishes with the request, it does a PostMessage back to the GUI thread that says "Request complete", which clears the "Query Active" flag.
 
In MDI/SDI, what you do is incorporate this into the OnUpdateCommandUI hanlders. For every element that needs to be disabled during the query, the test would be
 
void CMyDoc:OnUpdateCommandUI(CCmdUI * pCmdUI)
{
pCmdUI->Enable(!QueryActive && ...);
}
 
where ... was whatever used to enable it. Note that because this is per-document, if the document is busy, switching to any of its views leaves the controls disabled, but switching to another document allows the user to make progress doing something else.
 
In a dialog-based app, I centralize all my control updating in one function, updateControls(), so all that happens when the thread starts is that I call updateControls(), which disables everything I weant disabled based on the state of the query-active flag, and upon query completion I would call updateControls() again, which because the query-active flag is now FALSE would cause the controls formerly disabled to become enabled.
 
I find this the best way to handle the situation because it allows me to be selective about what I'm going to allow to remain active. The user can drag the window around, look at other documents, even change views in the current document, ask for help, exit the program, terminate the query, or whatever else makes sense, but not initiate another query or work on data from the previous query (for example, if I have a grid control, the grid control gets disabled as part of this; if I have multiple edit controls, dropdowns, check boxes, etc. that are based on or would modify the database contents, I would disable all those controls. WHen a control which is disabled has a lable, I change the label ID from IDC_STATIC to some other ID, create a control member variable for it, and disable it also. Makes for a nicer interface)
GeneralRe: GUI should not be active, but need to show snake-like-progressbar ?memberana_v12328 Mar '07 - 3:24 
Thanks a lot for helping.
 
Client App (VC++, MFC)
- its MDI App, One document - Multiple Views
- MDI but for now, only one document allowed to be open, so user (client) can work with one company (accounts) at a time
 
App Server (VC++, ATL/DCOM)
- Each user request is serverd by worker thread in a threadpool in server.
- When database is busy, thread tries for 8 times (every 500 milliseconds) and if fails, returns message indicatind database busy
- So user gets message in approx. 4 seconds, if database is busy (If database is not busy, then time depends on information)
 

That is why we need to show snak-like-progress
but wish to keep GUI in-active. (so for 4 seconds user has to wait)
 

So I used worker thread for requesting data and timer to show snake-like-progressbar in StatusBar. It works. It shows snake-like-progressbar in StatusBar properly.
But GUI (menus, toolbar buttons etc.) remains active.
 
I do not wish to keep Client GUI active.
How can I achieve that ?
 
Please help me.
Ana
GeneralMultiThreadingmemberAbhitha1 Feb '07 - 17:31 
Hi Joseph,
Can u explain mutithreading with example.pleaseeeeeeee
 
abiiiiiiiii

GeneralRe: MultiThreadingmemberJoseph M. Newcomer1 Feb '07 - 19:19 
No.
 
I have lots of examples of how to USE multithreading, but to *explain* multithreading with "an" example is nearly impossible. It serves many different roles, for many different reasons. Key is parallel computation. But the reasons for the concurrency vary highly. In some cases, you don't want to block the GUI thread (e.g., see my essay on my site about using serial ports with threads). Sometimes you do it to simplify logic (e.g., using an I/O Completion Port and server threads). Sometimes you use it to mass computation (e.g., graphics rendering). So there is no one way to explain it. But if you can ask something more specific, I can quite possibly point you to examples. But when I teach my course, this topic takes HOURS to discuss.
GeneralRe: MultiThreadingmemberAbhitha1 Feb '07 - 22:14 
In my case i use two threads.one for continuous image acquisition and another thread for image processing.My application is SDI.In image acquisition thread i also display the image.In image processing thread i
process the acquired data.i have the DLL for getting the data from the camera and Dll acquires 200 columns and 512 rows at a time.Now this Dll has to be called continuously and image has to be displayed simultaneously
and image has to be processed.
 
abiiiiiiiii

GeneralRe: MultiThreadingmemberJoseph M. Newcomer2 Feb '07 - 4:30 
OK, that makes it somewhat easier to discuss.
 
When you say you are displaying the image in the acquisition thread, how are you doing this?
 
Be careful of the word "simultaneously"; only on a true multiprocessor does this actually have meaning. Otherwise, there is always some delay due to timesharing of the threads.
 
The only coordination issue I see is between the image acquisition thread and the image processing thread. How are you you handling this? Or is this the key question.
 
So you've gone from a vague, impossible-to-answer question to a very specific description (which suggests you understand threading) with no question at all!
 
So how can I help?
Generali think there is an errormemberrsobies4 Jan '07 - 2:18 
at first i must thank u it is great article.
 
there is an error in CMyThread. if we call:
DWORD result = thread->Wait( );
 
from thread function CMyView::ComputeLikeCrazy( ) it will block the loop until CMyThread::Shutdown( ) is call becouse PauseEvent will not be signaled. to avoid it PausEvent should be signaled before the thread will be executed.
 
please correct me if i'm wrong.
GeneralRe: i think there is an errormemberJoseph M. Newcomer4 Jan '07 - 13:20 
You are correct, but look at where I do CreateEvent; note that I create it as signalled.
joe

GeneralAfxGetMainWnd( ) worked in VC++ 6.0, but not in VC++ 2005memberJaime Stuardo30 Sep '06 - 13:27 
Hi all...
 
I have an application that I developed in VC++ 6.0 that I have converted to VC++ 2005 (what was a real pain, but I finally could convert it).
 
I am using worker threads in it. A thread calls AfxGetMainWnd( ) to send messages to the main frame. All worked well when the application was compiled in VC++ 6.0, but it didn't when it was compiled in VC++ 2005 due to the problem you told about here in your article. To solve it, I did what you suggested, to use a variable that is set when the thread is created.
 
The question is: why did AfxGetMainWnd( ) work when I compiled using VC++ 6.0 and not using VC++ 2005? It's just curious.
 
Thanks
Jaime
GeneralRe: AfxGetMainWnd( ) worked in VC++ 6.0, but not in VC++ 2005memberBakaBug28 Dec '06 - 9:26 
Yeah I want know to .. nobody know ? I have same problem
GeneralRe: AfxGetMainWnd( ) worked in VC++ 6.0, but not in VC++ 2005memberJoseph M. Newcomer12 Jan '07 - 7:26 
And didn't I say in the article that it doesn't work?
GeneralCongrats .. Great ArticlememberJames F Kent30 Jul '06 - 3:34 
Thanks for such a great, insightful article. Your examples and discussion are very helpful.
 
Thanks again.
QuestionProblem with ThreadmemberTommyknocker198029 Jan '06 - 20:54 
Hello,
 
first of all thanks for your informative article!
 
Now I have the following problem, which takes some lines to explain:
In my program I use a thread, but sometimes appears some kind of deadlock.
 
Explanation of my application:
The thread changes cyclic the item text of a ListCtrl (with only 4 items).
This is done in a while-loop.
 
PROBLEM:
If I do some "wild actions", the application doesn't react anymore!

With "wild actions" I meen for example fast mouseklicks to open/close a popup menu (appears when clicking my toolbar)or some long clicks on the GUI...
 
Hint:
The application is written in embedded Visual C++ (for PDA).
 
Question:
Is my problem associated with the fast update of the list items with SetItemText?
 
Code:
My Thread is a static private member function of CMyClass and is started like this:
 

void CMyClass::StartThread(void)
{
// some commands ...
 
AfxBeginThread(ThreadProc, this, THREAD_PRIORITY_NORMAL);
 
// some commands ...
}

 
Here the thread's code and the code to update my ListCtrl:
 

UINT CMyClass::ThreadProc(LPVOID pParam)
{
CMyClass* pView = (CMyClass*) pParam;
ASSERT(pClass);
 
while(!pClass->m_bThreadStopped)
{
// some commands ...
 
// The call of the function to update the ListCtrl
pClass->UpdateList();
 
// some commands ...
}
 
ExitThread(0);
return 0;
}
 

void CMyClass::UpdateList(void)
{
// some commands ...

// Update the current lsit item (change the item text)
// m_pMyList is derived from CListCtrl
 
m_pMyList->UpdateItem(m_nCurrentItem, strNewItemText)

// some commands ...
}

 
Here's an extract of the code of CMyListCtrl (derived from CListCtrl):
 

void CMyListCtrl::UpdateItem(int nItem, CString strItemText)
{
// Create a new string on the heap
CString* pStrItemText= new CString(strItemText);
 
// The message WM_UPDATE_ITEM is user definde
// Send this message so that the list update is executed in the main thread
 
PostMessage(WM_UPDATE_ITEM, (WPARAM) nItem, (LPARAM) pStrItemText);
}
 

LRESULT CMyListCtrl::OnUpdateItem(WPARAM wParam, LPARAM lParam)
{
int nIndex = (int) wParam;
CString *pStrItemText= (CString *) lParam;
 
SetItemText(nIndex , 0, *pStrItemText);
 
// release the allocated heap
delete pStrItemText;
 
return 0L;
}

 
Can anybody help me?
What's wrong and how can I solve this problem respectively?
 
Thanks,
Tom

AnswerRe: Problem with ThreadmemberJoseph M. Newcomer12 Jan '07 - 7:31 
PostMessage does have a limit on the number of messages that can be posted. On a PDA this is probably very small. If you overflow it, it is likely bad things will happen, so the issue depends on the data rate. If you have a high data rate, you're in trouble. In this case, read my essay on the use of I/O Completion Ports for the basic philosophy of how to handle this, but since IOCPs don't exist in CE, you will have to also read my essay on the use of semaphores. Where I use GetQueuedCompletionStatus with a 0 timeout, you will use WaitForSingleObject on a semaphore with a 0 timeout. This gets the PostMessage queue out of the picture as a bottleneck
QuestionReturning listview items as they are retrievedmemberGuybrush23 Jan '06 - 9:45 
Hello Sir,
first of all let me apologize for my English...
I really appreciated your article, and it really gave me a professional insight about worker threads (the only hitch, MFC, I love WTL but, hey, you can't pretend everything adn, after all C++ still C++ Big Grin | :-D ).
I am trying to retrieve rows from a DB (using OleDB) in a virtual list box.
The class I am implementing basically has a method like this:
CString RetrieveItem(int row, int columns).
 
As the list will have to "display" litteraly millions of items, the idea was to handle the retrieving of rows in a separate thread, with the following logic:
1) The list view needs to display row 50 and requests it to the class.
2) The class checks if row 50 has already been cached and, if not starts a thread that would cache rows 25 to 75, then waits for the requested row to be available and returns it to the list.
2 - 1/2) The thread starts retreiving the rows and updates the cache.
 
My first idea BEFORE reading your article was to have the function loop until the requested row was cached. AFTER reading your article I felt all covered with shame Smile | :)
 
Then, keeping on reading, you suggest using WaitForSingleObject(), but in my case, this would still require a loop. I cannot allow RetrieveItem() to exit, because the list's waiting for its item, so the functions must keep on running until the row has been retrieved by the thread; however now I'd prefer not using a loop.
 
Could you suggest me the best way to procede in this case, or if I am not getting something?
I hope you won't find my request banal. I tried to interpret your article as best as I could!
 
Sorry for the lenghty post Smile | :)
 
Best Regards,
FL

AnswerRe: Returning listview items as they are retrievedmemberJoseph M. Newcomer29 Jan '06 - 16:01 
One simple approach is to simply disable the listbox/list control until the desired row has been retrieved. So there is no loop, and no wait. Instead, the thread has the retrieval loop, and when it does the PostMessage back to the GUI thread, the control is enabled either when the desired row is loaded, or the complete fetch is done. If this doesn't sound right for you, ask again.
GeneralRe: Returning listview items as they are retrievedmemberGuybrush30 Jan '06 - 10:06 
Hello and many thanks for the reply!
Unfortunately, I don't think this solution would work for me. The control in question is a virtual listview (ownerdata), and the requests for the item needed are, in this case, made from a function handling the LVN_GETDISPINFO message. So I don't think I can disable the listview when this happens (actually, that would be great, but I never seen this done and I don't think it is possible), so when the fetching thread starts the listview's waiting for the item because it needs to be displayed.
I did eventually implement everything, but I am not really satisfied by the way I did it. Let me explain what I did (if you find that I am long winded with the explanation its not because I think you need it, far from it, but because it helps me focus on errors...):
 
1) CMainFrame::OnListDisplayItem is called, which means the listview needs to display a particular item.
The function then calls DB.GetCacheAt (my db cache class), passing the needed row and column.
2) DB.GetCacheAt does the following
1 - while (TRUE) // UGLY, I know Frown | :(
2 - Check if the requested row its already cached. If it is, returns it (and exit).
3 - (if it gets here its because no item its cached). If a retrieving thread it's already running and the requested row its not it the range that is been retrieved asks it to stop (I use two volatile boolean for this: bThreadRunning and bThreadStop).
4 - If (!bThreadRunning) calculates the new range to retrieve. Something like FROM(nRequestedRow - (nMaxRowsToCache / 2)) TO(FROM + nMaxRowsToCache). Boundaries checking included. Then starts a new thread to retrieve the range.
5 - if (bThreadRunning) ... switch(::WaitForSingleObject(ThreadEvent, 500))
then ResetEvent()...
6 - Back to the top.
 
5 1/2 (the Thread) - The thread starts retrieving the range. However a parameter indicating the requested row was also passed to it, so that when that row has been retrieved, it can SetEvent and let the calling function return the item while keeping on caching the remaining rows.
 
So far, it works, even if it slower than I expected, but that has to do with the connection to the DB. One thing that I really seem not to get it's this: I originally set WaitForSingleObject's timeout to INFINITE. In theory, there should never be a possibility for the handle not to be signaled but I soon realized that the application would lock there waiting, and I still haven't figured out why. Even with the timeout to 500, I notice that the application hangs that 500 Ms when scrolling fast...
Secondly, that while(TRUE)... it works, it works, it works and I don't like it, I don't like it and I don't like it Smile | :)
 
Well, that is about it. I hope I haven't been to lengthy (or too boring).
 
Again, many thanks and best regards,
FL
GeneralRe: Returning listview items as they are retrievedmemberJoseph M. Newcomer30 Jan '06 - 11:38 
OK, I believe the part from step 2 to step 4. Now, the problem is that you block the GUI thread, you won't see any updates at all in your main thread, so that's a REALLY bad idea. Next, doing things like a WaitForSingleObject followed by a ResetEvent is an invitation to a synchronization failure, and should be avoided.
 
Going back to my original scheme, all you have to do is disable the window until the row is retrieved. You can add rows to it, but it will ignore user input until the desired row arrives. This is cleaner than doing a WaitForSingleObject.
 
All you have to do is wait for the worker thread to send a ntification that the desired row has arrived, which it can do as PostMessage after it does the PostMessage of the contents.
 
Not sure why a while(TRUE) is actually required.
 

GeneralRe: Returning listview items as they are retrievedmemberGuybrush31 Jan '06 - 10:36 
First, many thanks for the quick reply.
Second... I have been thinking a lot about your suggestion. Unfortunately I am affraid that it appears to be something that I don't know how to implement. I have used virtual listviews quite often, but in all cases the buffer was already there and this is my first implementation of a "buffer on demand".
 

Joseph M. Newcomer wrote:
Now, the problem is that you block the GUI thread

But that is exactly what is meant to happen, isn't it? When the listview requests the item, it's because it needs to display it on the screen. So the main thread cannot run until the view has its item. I mean, I need to return the item in the LV_ITEM structure when the list requests it, and only then. Am I wrong?
 
The reason for a separate thread caching the items it's because the cache it's bigger than the actual rows displayed, and the list can happily display its, say, 50 rows, while the worker thread's caching 500.
 
Joseph M. Newcomer wrote:
Next, doing things like a WaitForSingleObject followed by a ResetEvent is an invitation to a synchronization failure, and should be avoided.

My bad. In the hurry to code (this is a private project and I have very little free time Frown | :( ) I misread your article. Fixed it.
 

Joseph M. Newcomer wrote:
You can add rows to it, but it will ignore user input until the desired row arrives.

This is what I find difficult to get. Ok, let's forget about adding rows. The list is not going to do anything but display, and the number of items has already been set before the user can do anything. Being "virtual", now rows are really added to the list, just displayed.
Please correct me if I am wrong, but this is the way I see it:
When the user scrolls the listview, it notifies the main window with a LVN_GETDISPINFO. When this happens and the retrieving function starts (worker thread or not), as the GUI thread is waiting for the cache, the list will ignore user input anyway.
While the user is scrolling the list, he will not do anything else and in an "ideal world", it will never take too long for the desider row to be cached.
Then, once the requested row has been cached, everything starts working as before...
 

Joseph M. Newcomer wrote:
Not sure why a while(TRUE) is actually required.

I think I poorly explained the code I implemented. The while(TRUE) includes in it scope all the other steps I described, and it's not there on its own.
The reason while I make the steps loop continuosly is this:
The loop starts with the while(TRUE), then, if the row it's already in the cache the function returns and there is no loop. If a thread it's created, I make the loop wait with the WaitForSingleObject. When the WaitForSingleObject exit it's either because the row has been retrieved or (hopefully not) because of a timeout. Then the loop makes the function checks again for the cache and should hopefully return, because now the item should be available.
I think that I made you believe that the while(TRUE) was there just blocking everything without any sense. Sorry about that!
I didn't post any code before because I thought it would be too long, and because I prefer to learn this way (by discussing the implementation).
Anyway, I didn't give up on it, far from it. If you think that I should follow what you suggested, please tell me so and I will eventually understand how to code it Smile | :)
 
Best regards, and thanks for your patience!
FL.
GeneralRe: Returning listview items as they are retrievedmemberJoseph M. Newcomer1 Feb '06 - 4:42 
Separate thread caching: The issue about using a separate thread has nothing to do with the size of the cache. If that were the only concern, you could keep the cache in the main GUI thread. The purpose of a separate thread is to do lengthy, blocking computations without blocking the main GUI thread.
 
Blocking the GUI thread: you are confused. It should be ALWAYS considered an error to block the GUI thread. Note that you have mis-stated your problem. Your problem is to not allow the user to work in the list control until it has the record required. This is not at all the same as blocking the GUI thread. You have confused the idea of "prevent the user from interacting with a control" with a particular, usually incorrect, implementation "block the GUI thread". They are separate concerns, and blocking the thread is definitely not what you want to do (how else are you going to get elements drawn in that control?). If you needed to block the GUI thread, why do you have a separate thread for cache management. The two implementations together make no sense.
 
The scenario about the scrolling makes little sense. You keep going back to the seriously bad idea of blocking the GUI thread as the only possible way to prevent user interaction. This means that as soon as something previously uncached comes into play, the entire GUI stops dead. Scrolling stops. Everything stops. And screen update stops, all without any indication to the user about why this is so. If, for some reason, the thread is delayed (server load, perhaps?) the whole program is dead.
 
Actually, I'd probably be doing something like leaving the control enabled, but "disabling" the elements by not responding to them until the desired element is retrieved. This would be even better.
 
I understood the purpose of the while(TRUE), but I still don't see why a loop of that type is required. It shouldn't need to be checking the cache because the request has already been placed, and the cache thread should be posting messages back. These messages cannot be processed if the main GUI thread is blocked. So this is just another part of a blocking mechanism.
 
This is, I repeat, an attempt to impose a synchronous implementation on an inherently asynchronous problem domain. This leads to all sorts of problems, such as blocking the main GUI thread. The trick in Windows programming is to work the same as judo: don't fight your opponent; use the natural qualities of your opponent to achieve your goal. Using imposed synchrony is fighting the fundamental Windows paradigm. It is like a 280lp linebacker in a fight with a classic nerd: it gives the illusion of working. But pit that linebacker against a 350lb pro wrestler, and the brute force techniques don't work. Most of the time, people don't realize that their impression of the problem is that they're fighting something small, when they're fighting something much larger. Eventually, particularly under load conditions and error conditions, the opponent will pound you to jelly. This tends to make the end users, who are then pounded by one level of indirection, rather unhappy. So my philosophy on such designs is "how do I ignore the problem of synchrony?", which leads to much more interesting and useful solutions.
 
For example, in one situation I had to enumerate the number of controllers on an embedded network. This could take several minutes. I used a similar technique to the one I described above, except I didn't disable the listbox. As I inserted each item, the user could actually double-click on it and start working on it while the remainder enumerated. So the GUI was live the entire time. I hadn't even considered something like blocking the main GUI thread. It worked very well indeed, from the viewpoint of the users. So I'm not trying to describe some theoretical scenario (this was in a program I wrote about six or seven years ago). I've used this technique many times since.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 6 Apr 2001
Article Copyright 2000 by Joseph M. Newcomer
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid