C++线程中与pthread_exit等效的函数是什么?

3

我需要一个C++等效的 pthread_exit 函数,以便创建一个函数,当调用时可以使调用线程退出。

在 C 语言中,使用 pthreads.h 头文件,我只需调用 pthread_exit 即可。我是 C++ 的新手,需要使用其 <thread> ,但我找不到类似的函数。

我使用的是 C++17,在 Linux 上编译代码(可能也要在 MacOs 上编译)。


3
听起来像是反模式。 - Richard Hodges
我希望调用该函数的线程退出。因此,当工作线程从任务队列中弹出这个终止任务时,它将退出。 - ninazzo
简而言之:抛出异常。下面的答案说,终止线程的唯一安全方式是让它的顶层函数返回。抛出异常是您可以从发现需要终止的点返回到顶层的方法。 - Solomon Slow
@SolomonSlow OP的平台都符合POSIX标准,因此可以假设pthread_exit可以通过std::thread::native_handle访问。我不知道,如果允许用户访问本机句柄是不安全的,那么为什么std lib设计者会这样做呢? - dhanushka
@dhanushka,Re,“为什么标准库设计者要这样做?”他们怎么可能阻止你链接任何想要的库?调用任何想要调用的函数(包括任何操作系统函数)?C++不是那些旨在让新手犯错误变得困难的玩具学习编程环境之一。它给程序员提供了足够的绳子来自我毁灭。 - Solomon Slow
显示剩余6条评论
2个回答

3

没有直接的方法来做到这一点 - 终止线程的正常方式是从调用线程启动时调用的顶层函数返回 - 但您可以通过在想要终止线程的地方抛出异常并在线程的顶层函数中捕获它来实现相同的效果。这样做的好处是线程的堆栈得以正确卸载并调用任何相关的析构函数。

例如:

#include <iostream>
#include <thread>
#include <exception>

class thread_exit_exception : public std::exception {};

void thread_subfunc ()
{
    std::cout << "Entering thread_subfunc\n";
    thread_exit_exception e;
    throw e;
    std::cout << "Leaving thread_subfunc (never executed)\n";
}

void thread_func ()
{
    std::cout << "Entering thread_func\n";
    try
    {
        thread_subfunc ();
    }
    catch (const thread_exit_exception&)
    {
    }
    std::cout << "Leaving thread_func\n";
}

int main()
{
    std::cout << "Entering main\n";
    std::thread t = std::thread (thread_func);
    t.join ();
    std::cout << "Leaving main\n";
}

输出:

Entering main
Entering thread_func
Entering thread_subfunc
Leaving thread_func
Leaving main

实时演示


1

C++对其调用堆栈的依赖性比C更强。C++程序通常使用RAII,这意味着资源绑定到经常存在于堆栈上的对象。这些对象的用户期望这些对象被正确销毁。如果一个函数创建了一个堆栈对象,则它期望在将来的某个时刻,控制会返回到该函数并销毁堆栈对象。

因此,没有机制使具有一定堆栈深度的线程简单退出。只有当达到传递给thread构造函数的函数的末尾时,std::thread才会结束(从线程函数发出的异常会调用std::terminate)。

鉴于此,最好重新组织代码,以便从调用图中的任意位置都不需要导致线程函数退出。使得您希望当前线程退出的唯一点是在线程的主函数中。

例如,线程池的典型工作方式是每个线程的主函数进入睡眠状态,等待某种形式的任务被分配给该线程。当任务可用时,它执行该任务。在任务完成后,它检查是否有新任务,如果没有,则返回睡眠状态,直到任务准备就绪。
在这样的线程池中,没有线程会停止。单个任务会停止,但实际的 std::thread 是永恒的(或者至少与池一样长寿)。
如果任务需要终止,则此类终止实质上表示无法执行任务。在 C++ 中,这被拼写为 "抛出异常"。主线程将所有任务调用放在 try 块中,使用特定异常类型的 catch 块。然后它可以向任何人报告任务失败,然后去检查新任务。
这确保了任务的调用堆栈被清除。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接