使用线程取消搜索

cancelling a search using threads

本文关键字:搜索 取消 线程      更新时间:2023-10-16

我是多线程的新手。我在unix上使用c++。

在下面的代码中,runSearch()需要很长时间,我希望能够在"cancel==true"后立即终止搜索。函数cancelSearch由另一个线程调用。

解决这个问题的最佳方法是什么?

谢谢你。。

------------------这是现有的代码-----------------------

    struct SearchTask : public Runnable
    {
        bool cancel = false;
        void cancelSearch()
        {
           cancel = true;
        }
        void run()
        {
            cancel = false;
            runSearch();
            if (cancel == true)
            {
                return;
            }
            //...more steps.
        }
    }

编辑:为了更清楚,假设runSearch()运行需要10分钟。1分钟后,cancel==true,然后我想立即退出run(),而不是再等9分钟等待runSearch()完成。

您需要在整个搜索操作中不断检查标志。类似这样的东西:

    void run()
    {
        cancel = false;            
        while (!cancel)
        {
            runSearch();
            //do your thread stuff...
        }
    }

您已经提到不能修改runSearch()。pthreads有一个pthread_setcancelstate()函数,但我认为这不安全,尤其是对于需要RAII语义的C++代码。

安全线程取消必须是协作的。被取消的代码必须知道取消,并且能够在自己之后进行清理。如果代码不是设计用来做这件事的,而是简单地终止了,那么你的程序可能会表现出未定义的行为。

由于这个原因,C++的std::thread不提供任何线程取消方法,相反,代码必须使用显式取消检查来编写,正如其他答案所示。

创建一个接受操作/委托的泛型方法。让每一步都是非常微小和具体的。向通用方法发送您认为是"步骤"的委托/操作。在泛型方法中,检测cancel是否为true,如果为true则返回。因为如果取消,步骤很小,线程应该不会花很长时间就死了。

这是我能给出的最好的建议,没有任何步骤的代码。

另请注意:

 void run()
{
    cancel = false;
    runSearch();
    while (!cancel)
    {
        //do your thread stuff...
    }
}

不起作用,因为如果你正在做的不是迭代,它会在检查之前运行整个线程!取消就像我说的,如果你能添加更多关于步骤的细节,那么给你建议会更容易。在处理要停止或终止的线程时,最好将代码分成非常小的步骤。

基本上,您必须到处轮询cancel标志。你还可以使用其他技巧,但它们更特定于平台,比如线程取消,或者不够通用,比如中断。

cancel需要是一个原子变量(就像std::atomic中一样,或者只是用互斥锁保护它),否则编译器可能只将值缓存在寄存器中,而看不到来自另一个线程的更新。

读取响应是正确的-仅仅因为您在线程中调用了阻塞函数并不意味着它神奇地变成了非阻塞调用。线程可能不会中断程序的其余部分,但它仍然需要等待runSearch调用完成。

好吧,有一些方法可以解决这个问题,但它们不一定安全。

您可以显式地终止线程。在Windows上,您可以使用TerminateThread()来终止线程执行。听起来不错吧?好吧,除了使用它非常危险之外——除非你确切地知道被杀线程中所有的资源和调用都在进行什么,否则你可能会发现自己的应用程序下次无法正常工作。例如,如果runSearch打开一个DB连接,TerminateThread调用将不会关闭它。这同样适用于内存、加载的dll以及它们使用的所有内容。它是为杀死完全没有响应的线程而设计的,这样你就可以关闭程序并重新启动它

考虑到以上情况,以及强烈建议您不要使用它,下一步是以外部方式调用runSearch-如果您在一个单独的进程中运行阻塞调用,那么该进程可以被杀死,而且您不会对其他一切都进行窃听。这个进程死了,清除了它的内存、堆、任何加载的dll等等。因此,在线程内部,调用CreateProcess并等待句柄。你需要IPC上的一些表格(可能最好不要使用共享内存,因为当你终止进程时重置它可能会很麻烦)来将结果传输回你的主应用程序。如果您需要终止此进程,请在其句柄上调用ExitProcess(或在Linux中退出)请注意,这些退出调用需要在进程内部调用,因此您需要在进程内运行一个线程来进行阻塞调用。你可以从外部终止一个进程,但同样,这是危险的——不像杀死一个线程那么危险,但你仍然可能偶尔出错。(使用TerminateProcess或kill)