那么,你不应该在一个线程的执行中杀死它,原因如下:
- 一个线程可能已经打开了一个文件进行写入,如果它被杀死,该文件将不会被关闭。这是个麻烦。
- 一个线程可能已经获得了一个计算机资源的锁,仅供其使用。如果该线程被杀死,资源将继续被锁定,而其他线程和进程将不能使用这些资源。
- 分配的内存必须被释放。线程可能已经为某种目的分配了一些内存。如果该线程被杀死,该内存将保持虚假的分配,其他线程和进程无法使用。这就是内存泄漏。
这些原因和其他任何意味着,如果一个线程被杀死,它可能已经获得的资源将不会被释放给其他线程和进程使用。当一个线程自然完成时,任何获得的资源都会被释放。
杀死一个线程的典型动机是,用户不再需要这个线程的结果了。
有一些好消息。C++20是当今C++的最新版本。C++20的线程类有一些组件,可以在一个线程的自然结束之前释放其资源,并在其自然结束之前停止它。这样一来,C++是在停止线程而不是杀死线程。换句话说,C++20是在负责任地杀死线程。资源的释放和线程的停止是自动的。注意:不是所有的线程都能以这种方式停止。这样的线程会自然完成,即使有人试图停止它们。
线程库有以下类,用于随着资源的释放而停止:stop_token,stop_source,和stop_callback。这些类中的每一个都可以有实例化的对象。然而,本教程中只考虑stop_token和stop_source。
用g++编译器运行一个线程程序的命令,对于C+20来说,应该类似于。
g++ -std=c++2a temp.cpp -lpthread -o temp
本教程解释了如何停止一个资源被释放的线程。在资源被释放的情况下停止一个线程是一种负责任的杀死线程的方式。本教程首先总结了对线程的控制。
文章内容
线程编码总结
在C++中,一个正在运行的程序是一个进程。一个线程是一个进程的子进程。一个简单的C++程序只有一个线程,那就是main()函数。main()函数不是一个正式声明的线程。同一程序的任何其他线程都必须被正式声明。一个程序中可以有多个线程。
一个线程是由线程库中的一个线程类实例化的。线程对象声明的第一个参数是一个顶级函数的名称。该顶层函数是有效的线程。当对象被实例化时,顶层函数开始执行(运行)。
有一个调用线程和一个被调用线程。不幸的是,如果被调用线程没有从被调用线程的函数体中加入,那么调用线程可能在被调用线程没有完成自己的执行的情况下完成其执行。这就意味着麻烦。所以,调用线程的函数体应该总是在被调用线程的实例化之后加入被调用线程。
在下面的程序中,一个线程对象被实例化了,使用的是顶级函数fn()。
#include <iostream>
#include <thread>
using namespace std;
void fn() {
cout <<"first code segment of thread" <<endl;
cout <<"second code segment of thread" <<endl;
}
int main()
{
thread thr(fn);
thr.join();
return 0;
}
输出结果是。
first code segment of thread
second code segment of thread
请注意包含了线程库。注意main函数的第一个和第二个语句是如何编码的。main()函数是主线程。 fn()是一个顶层函数。
jthread类
jthread是一个定义在线程库中的类。它和线程类一样,但它的优点是可以通过释放资源来停止一个线程。它有成员函数来返回一个stop_token对象和一个stop_source对象。这些成员函数是
stop_source get_stop_source()
stop_token get_stop_token()
它还有一个成员函数用于提出停止请求,即
bool request_stop()
截至目前,在2021年10月,许多C++编译器仍在实现jthread类。然而,当你的编译器已经实现了jthread类时,下面给出的代码样本应该可以工作。
请求停止一个线程
停止线程意味着停止顶层函数的运行。请求停止意味着该线程应尽快停止。如果请求没有被批准,线程将运行到完成,而不会在其结束前停止。
如上所述,从jthread实例化出来的线程具有负责任地杀死一个线程的功能(阻止一个线程释放其资源)。请求这种停止的成员函数是。
bool request_stop()
如果请求被接受,其返回值为true,否则为false。接受请求并不保证该线程会尽快停止。可能无法实现该请求,线程将不会停止,直到其自然结束。也就是说,返回true并不意味着停止是可能的。下面的程序说明了jthread对象的这个成员函数的使用。
#include <iostream>
#include <thread>
using namespace std;
void fn() {
cout <<"first code segment of thread" <<endl;
cout <<"second code segment of thread" <<endl;
}
int main()
{
jthread thr(fn);
thr.request_stop();
thr.join();
return 0;
}
这个程序与上述程序相似,但有两点。
- 线程,thr是从jthread类中实例化出来的。
- 尽快停止线程的语句(请求)被放在join()语句之前。在这种情况下,调用线程要停止被调用线程的继续执行。
停止是可能的吗?
在某些情况下,停止线程是不可能的。然而,程序员无法知道一个线程是否可以停止。在这种情况下,程序员必须要进行查询。stop_source有一个成员函数。
bool stop_possible() const
如果返回值为真,那么就有可能在线程的自然结束前停止它。如果返回值为false,则不可能在线程自然结束前停止它。jthread有一个方法可以返回stop_source对象。
所以,在停止线程之前询问是否可以停止该线程可能会更好。下面的程序说明了这一点。
#include <iostream>
#include <thread>
using namespace std;
void fn() {
cout <<"first code segment of thread" <<endl;
cout <<"second code segment of thread" <<endl;
}
int main()
{
jthread thr(fn);
stop_source ss = thr.get_stop_source();
if (ss.stop_possible())
thr.request_stop();
else
cout <<"Thread could be stopped!" <<end;
thr.join();
return 0;
}
停止的代码段已被放在join语句之前。
是否已提出停止请求?
如果有可能停止一个线程,这仍然不能保证request_stop()语句能成功地在线程的自然结束前停止它。如果线程没有像希望的那样在其自然结束前停止,那么程序员可能想知道该线程是否被请求用 request_stop() 语句停止。
stop_token对象有一个成员函数。
bool stop_requested()
这个函数在有停止请求的情况下返回true,否则返回false。jthread对象可以返回一个stop_token对象,其成员函数如下。
stop_token get_stop_token() const
下面的main()函数代码说明了如何知道是否已经发出了request_stop。
int main()
{
jthread thr(fn);
stop_source ss = thr.get_stop_source();
if (ss.stop_possible())
thr.request_stop();
else
cout <<"Thread could be stopped!" <<end;
stop_token st = thr.get_stop_token();
if (st.stop_requested())
cout <<"Still waiting for thrread to stop." <<endl;
else
thr.request_stop();
thr.join();
return 0;
}
所有的停止代码都在join语句之前。不要混淆 request_stop() 和 stop_requested() 函数。
结论
在C++20及以上版本中,可以负责任地杀死一个线程。这意味着在停止线程的同时,释放该线程的资源。线程库中有stop_token、stop_source、stop_callback和jthread类,用于负责任地杀死一个线程。要使用stop_token、stop_source和stop_callback的实例化对象,需要用jthread类创建线程。jthread类在线程库中,它必须包含在C++程序中。
jthread类有成员函数来返回stop_token和stop_source对象。jthread类本身也有成员函数,request_stop(),用来停止一个线程。这个请求很可能被批准,但并不保证一定会被批准。如果请求被批准了,那么在一切都相同的情况下,线程会尽快停止,而不会达到其自然结束。
stop_source对象可用于了解停止线程是否可能。stop_token对象可以用来知道是否已经发出了request_stop()。一旦发出停止请求,就不能撤回(后续的停止请求没有效果)。