1

These days I'm trying to learn more things about threads in windows. I thought about making this practical application:

Let's say there are several threads started when a button "Start" is pressed. Assume these threads are intensive (they keep running / have always something to work on).

This app would also have a "Stop" button. When this button is pressed all the threads should close in a nice way: free resources and abandon work and return the state they were before the "Start" button was pressed.

Another request of the app is that the functions runned by the threads shouldn't contain any instruction checking if the "Stop" button was pressed. The function running in the thread shouldn't care about the stop button.

Language: C++

OS: Windows

Problems:

WrapperFunc(function, param)    
{
     // what to write here ?
     // if i write this:
     function(param);
     // i cannot stop the function from executing
}
  • How should I construct the wrapper function so that I can stop the thread properly? ( without using TerminateThread or some other functions )

  • What if the programmer allocates some memory dynamically? How can I free it before closing the thread?( note that when I press "Stop button" the thread is still processing data) I though about overloading the new operator or just imposing the usage of a predefined function to be used when allocating memory dynamically. This, however, means that the programmer who uses this api is constrained and it's not what I want.

Thank you

Edit: Skeleton to describe the functionality I'd like to achieve.

struct wrapper_data
{
      void* (*function)(LPVOID);
      LPVOID *params;
};

/* 
this function should make sure that the threads stop properly
( free memory allocated dynamically etc )
*/
void* WrapperFunc(LPVOID *arg)    
{
     wrapper_data *data = (wrapper_data*) arg;
     // what to write here ?
     // if i write this:
     data->function(data->params);
     // i cannot stop the function from executing
     delete data;
}

// will have exactly the same arguments as CreateThread
MyCreateThread(..., function, params, ...)
{
    // this should create a thread that runs the wrapper function
    wrapper_data *data = new wrapper_data;
    data->function = function;
    data->params = params;

    CreateThread(..., WrapperFunc, (LPVOID) wrapper_data, ...);
}

thread_function(LPVOID *data)
{
   while(1)
   {
        //do stuff
   }
}

// as you can see I want it to be completely invisible 
// to the programmer who uses this
MyCreateThread(..., thread_function, (LPVOID) params,...);
Dan Lincan
  • 1,065
  • 2
  • 14
  • 32

3 Answers3

0

One solution is to have some kind of signal that tells the threads to stop working. Often this can be a global boolean variable that is normally false but when set to true it tells the threads to stop. As for the cleaning up, do it when the threads main loop is done before returning from the thread.

I.e. something like this:

volatile bool gStopThreads = false;  // Defaults to false, threads should not stop

void thread_function()
{
    while (!gStopThreads)
    {
        // Do some stuff
    }

    // All processing done, clean up after my self here
}

As for the cleaning up bit, if you keep the data inside a struct or a class, you can forcibly kill them from outside the threads and just either delete the instances if you allocated them dynamically or let the system handle it if created e.g. on the stack or as global objects. Of course, all data your thread allocates (including files, sockets etc.) must be placed in this structure or class.


A way of keeping the stopping functionality in the wrapper, is to have the actual main loop in the wrapper, together with the check for the stop-signal. Then in the main loop just call a doStuff-like function that does the actual processing. However, if it contains operations that might take time, you end up with the first problem again.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 2
    Consider using the 'volatile' keyword to prevent the compiler from causing the boolean to be cached and held in a register, preventing value changes from becoming visible to the threads. –  Jun 14 '12 at 10:26
  • The problem is that I want the user to not care about stopping in the thread function, just write the functionality. – Dan Lincan Jun 14 '12 at 10:28
  • @QtLearner Added a paragraph about another way of cleaning up after the threads. – Some programmer dude Jun 14 '12 at 10:33
  • @QtLearner You can't make it completely "invisible" to the users of your system, you can't kill a thread nicely from the outside, and have it automatically clean up after itself. The thread _has_ to look for a signal of some kind. If it's a global variable, of a member-variable in a class or something else completely doesn't matter. – Some programmer dude Jun 14 '12 at 10:46
  • Agree. Please comment on my answer. –  Jun 14 '12 at 10:47
  • Check my skeleton, maybe now you can think of an idea. So far I thought for just one day for a solution. I won't give up that fast. – Dan Lincan Jun 14 '12 at 10:48
  • You may come to realise that thread wrappers do not work. Thread usage is so versatile that any attempt at encapsulation will just cause limitations. Any programmer who is going to be writing threading code will be quite capable of calling to create the thread themselves and set up their own synchronisation points .etc –  Jun 14 '12 at 10:54
0

See my answer to this similar question:

How do I guarantee fast shutdown of my win32 app?

Basically, you can use QueueUserAPC to queue a proc which throws an exception. The exception should bubble all the way up to a 'catch' in your thread proc.

As long as any libraries you're using are reasonably exception-aware and use RAII, this works remarkably well. I haven't successfully got this working with boost::threads however, as it's doesn't put suspended threads into an alertable wait state, so QueueUserAPC can't wake them.

Community
  • 1
  • 1
Roddy
  • 66,617
  • 42
  • 165
  • 277
0

If you don't want the "programmer" of the function that the thread will execute deal with the "stop" event, make the thread execute a function of "you" that deals with the "stop" event and when that event isn't signaled executes the "programmer" function...

In other words the "while(!event)" will be in a function that calls the "job" function.

Code Sample.

typedef void (*JobFunction)(LPVOID params); // The prototype of the function to execute inside the thread

struct structFunctionParams
{
    int iCounter;
    structFunctionParams()
    {
        iCounter = 0;
    }
};

struct structJobParams
{
    bool bStop;
    JobFunction pFunction;
    LPVOID pFunctionParams;
    structJobParams()
    {
        bStop = false;
        pFunction = NULL;
        pFunctionParams = NULL;
    }
};

DWORD WINAPI ThreadProcessJob(IN LPVOID pParams)
{
    structJobParams* pJobParams = (structJobParams*)pParams;
    while(!pJobParams->bStop)   
    {
        // Execute the "programmer" function
        pJobParams->pFunction(pJobParams->pFunctionParams);
    }   

    return 0;
}


void ThreadFunction(LPVOID pParams)
{
    // Do Something....
    ((structFunctionParams*)pParams)->iCounter ++;
}


int _tmain(int argc, _TCHAR* argv[])
{   
    structFunctionParams stFunctionParams;

    structJobParams stJobParams;
    stJobParams.pFunction = &ThreadFunction;
    stJobParams.pFunctionParams = &stFunctionParams;




    DWORD dwIdThread = 0;
    HANDLE hThread = CreateThread( 
        NULL,
        0, 
        ThreadProcessJob,
        (LPVOID) &stJobParams, 0, &dwIdThread);
    if(hThread) 
    {   
            // Give it 5 seconds to work
        Sleep(5000);
        stJobParams.bStop = true; // Signal to Stop
        WaitForSingleObject(hThread, INFINITE); // Wait to finish
        CloseHandle(hThread);
    }
}
João Augusto
  • 2,285
  • 24
  • 28
  • Now you are getting into a whole work/job pool system. Additionally, this doesn't solve the problem of cancelling a job during a busy loop. –  Jun 14 '12 at 11:00
  • if I use while(!event) the user function(job) will run and will never exit ( notice the while(1) loop in thread_function) => the wrapper function is useless – Dan Lincan Jun 14 '12 at 11:05
  • @infact - Cancelling wasn't part of the problem :) – João Augusto Jun 14 '12 at 11:06
  • @QtLearner - That's why I said that the while condition isn't in the job function – João Augusto Jun 14 '12 at 11:07
  • Please provide some code, I don't understand. The problem is that when I run the thread_function (data->function(data->params); line ) I lose control of the execution. – Dan Lincan Jun 14 '12 at 11:12
  • If you have this function to execute it will not work. void ThreadFunction(LPVOID pParams) { while(1) { // Do Something.... } } – Dan Lincan Jun 14 '12 at 13:24
  • @QtLearner - You can't have a function doing a infinite loop without a way to leave it and at the same time a "clean" exit... also I still don't understand exactly what you are trying to achieve that prevents a "worker thread" of having a parameter to check for exit/cancel. The worker don't need to know how the exit/cancel is "implemented", he just needs to be able to cancel the execution if needed. – João Augusto Jun 14 '12 at 16:47