1

这些天来,我正在尝试了解有关 Windows 中线程的更多信息。我想过做这个实际的应用程序:

假设按下“开始”按钮时启动了多个线程。假设这些线程是密集的(它们一直在运行/总是有一些工作要做)。

这个应用程序还有一个“停止”按钮。当按下此按钮时,所有线程都应该以一种很好的方式关闭:释放资源并放弃工作并返回它们在按下“开始”按钮之前的状态。

该应用程序的另一个要求是线程运行的函数不应包含任何检查“停止”按钮是否被按下的指令。线程中运行的函数不应该关心停止按钮。

语言:C++

操作系统:Windows

问题:

WrapperFunc(function, param)    
{
     // what to write here ?
     // if i write this:
     function(param);
     // i cannot stop the function from executing
}
  • 我应该如何构造包装函数以便可以正确停止线程?(不使用 TerminateThread 或其他一些功能)

  • 如果程序员动态分配一些内存怎么办?如何在关闭线程之前释放它?(请注意,当我按下“停止按钮”时,线程仍在处理数据)我想重载新运算符或只是强加使用预定义函数以在动态分配内存时使用. 然而,这意味着使用这个 api 的程序员受到了限制,这不是我想要的。

谢谢

编辑:骨架来描述我想要实现的功能。

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,...);
4

3 回答 3

0

请参阅我对这个类似问题的回答:

如何保证快速关闭我的 win32 应用程序?

基本上,您可以使用 QueueUserAPC 对引发异常的 proc 进行排队。异常应该一直冒泡到您的线程过程中的“捕获”。

只要您使用的任何库都具有合理的异常感知能力并使用 RAII,它就非常有效。但是,我还没有成功地使用 boost::threads 进行此操作,因为它不会将挂起的线程置于可警报的等待状态,因此 QueueUserAPC 无法唤醒它们。

于 2012-06-14T10:55:15.283 回答
0

如果您不希望线程将执行的函数的“程序员”处理“停止”事件,请让线程执行处理“停止”事件的“你”函数以及该事件何时发生t 发出信号执行“程序员”功能...

换句话说,“while(!event)”将在一个调用“job”函数的函数中。

代码示例。

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);
    }
}
于 2012-06-14T10:55:33.920 回答
0

一种解决方案是使用某种信号告诉线程停止工作。通常这可以是一个全局布尔变量,通常是false,但当设置为true它时,它会告诉线程停止。至于清理,在线程主循环完成后再从线程返回之前进行。

即是这样的:

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
}

至于清理位,如果您将数据保存在结构或类中,则可以从线程外部强行杀死它们,delete如果动态分配它们,则可以只杀死实例,或者如果在堆栈上创建,则让系统处理它或作为全局对象。当然,你的线程分配的所有数据(包括文件、套接字等)都必须放在这个结构或类中。


在包装器中保留停止功能的一种方法是在包装器中包含实际的主循环,以及对停止信号的检查。然后在主循环中调用一个类似doStuff的函数来进行实际处理。但是,如果它包含可能需要时间的操作,您最终会再次遇到第一个问题。

于 2012-06-14T10:24:34.133 回答