Loser-HomeWork
Loser-HomeWork copied to clipboard
关于 `C++CoreGuidelines` 对于 `detach` 的描述
std::cout 的生存期与进程的生存期绑定。这意味着,在屏幕上打印出“C++11”之前,std::cout 对象可能已经消失了。
英文原书都一个意思
std::cout’s lifetime is bound to the lifetime of the process. This means that the thread thr may be gone before std::cout prints C++11 onscreen.
在新版的 C++CoreGuidelines F.53 中,倒是没有了这段描述。
我想讨论目前描述的合理性,用户使用 detach 应该必然要求且保证 detach 的线程在主线程、进程执行完毕之前,就会结束。如果没有,那也不单单是 std::cout 会出现问题了。
比如b乎聊过的。
@frederick-vs-ja @rsp4jack @dynilath @Juvwxyz @Seedking
脱附的线程可能还能会持有直接从操作系统获得的资源;此时合法性取决于 OS 的设计。
另外我工作中参与的项目经常有“线程已经 detach
,但其最后的读写与主线程同步,从而之后线程晚点结束也无所谓”的情况。
此时合法性取决于 OS 的设计
这段话描述的很好,也是我想的。
线程已经 detach,但其最后的读写与主线程同步,从而之后线程晚点结束也无所谓
我能理解的应该是:已经被 detach 的线程,其最后的操作执行,主线程还没结束,也就能保证被 detach 的线程的正确执行,在此之后它即使晚点结束也无所谓,毕竟也都执行完了需要的操作,对吧?
脱附的线程可能还能会持有直接从操作系统获得的资源;此时合法性取决于 OS 的设计。
那么问题来了,能否找到一个平台,能让已经被 detach 的线程,且主线程和进程结束,这个线程依然能成功执行一些任务?
脱附的线程可能还能会持有直接从操作系统获得的资源;此时合法性取决于 OS 的设计。
那么问题来了,能否找到一个平台,能让已经被 detach 的线程,且主线程和进程结束,这个线程依然能成功执行一些任务?
你看下哪个平台不行,注意不要依赖标准库的对象,后续读写都依赖 OS API。
b乎里写了的那个示例是使用的 system
函数,执行的命令,测试了 windows11 和 wsl2 的 ubuntu222.04 都不行。
#include<iostream>
#include<thread>
#include<unistd.h>
#include<fcntl.h>
using namespace std::chrono_literals;
void f(){
std::this_thread::sleep_for(2s);
int fd = open("test2.txt",O_RDWR+O_CREAT);
write(fd,"🤣🤣",sizeof("🤣🤣"));
}
int main(){
std::puts("main");
std::thread t{f};
t.detach();
std::puts("main end");
}
ubuntu22.04 无效,没有办法创建出新的文件进行读写。
如果把 std::this_thread::sleep_for(2s);
这段代码移动到 main 函数的末尾,则可以正确创建出文件,查看内容也合理。
root@Mq-B:/test# g++ detach2.cc -lpthread -o detach_test2 root@Mq-B:/test# ./detach_test2 main main end root@Mq-B:/test# cat test2.txt 🤣🤣root@Mq-B:/test#
如果把
std::this_thread::sleep_for(2s);
这段代码移动到 main 函数的末尾,则可以正确创建出文件,查看内容也合理。
搞那么复杂干啥……你调用pthread_exit的函数把主线程干掉不就行了。主线程里会处理申请的资源(当然也包括申请的线程),结束掉主线程就不会清理资源。
搞那么复杂干啥……你调用pthread_exit的函数把主线程干掉不就行了。主线程里会处理申请的资源(当然也包括申请的线程),结束掉主线程就不会清理资源。
教教,写个示例给窝,
教教,写个示例给窝,
#include<iostream>
#include<cstdint>
#include<vector>
#include<fstream>
#include<thread>
#include<chrono>
#ifdef _MSC_VER
#include<windows.h>
#else
#include<pthread.h>
#endif
void test_thread() {
std::cout << "test thread" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
// 在此处打断点查看进程会发现只有一个线程
std::cout << "after sleep" << std::endl;
}
int main() {
std::cout << "main" << std::endl;
std::thread th(test_thread);
th.detach();
std::cout << "after thread" << std::endl;
#ifdef _MSC_VER
ExitThread(0);
#else
pthread_exit(0);
#endif
}
int main() { std::cout << "main" << std::endl; std::thread th(test_thread); th.detach(); std::cout << "after thread" << std::endl; #ifdef _MSC_VER ExitThread(0); #else pthread_exit(0); #endif }
ExitThread(0);
会导致问题
例如在main前面加一个
struct foo {
foo() { std::cout << "foo()\n"; }
~foo() { std::cout << "~foo()\n"; }
} X;
int main(){
程序会跳过X的析构,所以这个ExitThread(0)其实是把后面的资源回收工作全都跳过了从而救活了这个detach的thread
具体机制我看要么是libstdc++把回收放在了一个另外的地方调度要么是pthread_exit做了一些转移资源的工作
ExitThread(0);
会导致问题程序会跳过X的析构,所以这个ExitThread(0)其实是把后面的资源回收工作全都跳过了从而救活了这个detach的thread
具体机制我看要么是libstdc++把回收放在了一个另外的地方调度要么是pthread_exit做了一些转移资源的工作
主线程都没了,无法处理资源释放了。可以自己创建一个线程去释放资源:
#include<iostream>
#include<cstdint>
#include<vector>
#include<fstream>
#include<thread>
#include<chrono>
#ifdef _MSC_VER
#include<windows.h>
#else
#include<pthread.h>
#endif
void test_thread() {
std::cout << "test thread\n";
std::this_thread::sleep_for(std::chrono::seconds(3));
// 在此处打断点查看进程会发现只有两个线程,还有一个test_exit
std::cout << "after sleep\n";
}
void test_exit() {
std::cout << "test exit\n";
std::this_thread::sleep_for(std::chrono::seconds(5));
exit(0);
}
struct foo {
foo() { std::cout << "foo()\n"; }
~foo() { std::cout << "~foo()\n"; }
} X;
int main() {
{
std::cout << "main\n";
std::thread th(test_thread);
th.detach();
std::thread ex(test_exit);
ex.detach();
std::cout << "after create thread\n";
}
#ifdef _MSC_VER
ExitThread(0);
#else
pthread_exit(0);
#endif
}
主线程借尸还魂?(大雾
你看看这3秒后test_thread,5秒后test_exit,是不是怎么看怎么是join。该join就join。
我认为是不能在main里又ExitThread又std::exit的,显然会导致不少资源double free
ExitThread:
When this function is called (either explicitly or by returning from a thread procedure), the current thread's stack is deallocated, all pending I/O initiated by the thread that is not associated with a completion port is canceled, and the thread terminates. The entry-point function of all attached dynamic-link libraries (DLLs) is invoked with a value indicating that the thread is detaching from the DLL.
std::exit:
Several cleanup steps are performed:
- The destructors of objects with thread ... b) Functions registered with std::atexit are called ...
- All C streams are flus...
- Files created by std::tmpfile are removed ...
你看看这3秒后test_thread,5秒后test_exit,是不是怎么看怎么是join。该join就join。
我认为是不能在main里又ExitThread又std::exit的,显然会导致不少资源double free
首先,我给出的例子并不是要告诉你推荐你在主线程里使用 ExitThread
,而是想说可以用这个函数可以结束掉主线程来使得主线程不去执行 main
函数结束后的进程资源清理工作。
至于资源释放问题是并不存在 double free 问题,因为 ExitThread
并不会调用 C++ 的析构函数。清理当前线程的栈区也与其他线程无关。所以会存在资源泄露,而不是 double free。
你说的其他问题:
- 并不是3秒后test_thread,5秒后test_exit,
std::this_thread::sleep_for
的执行都是在独立线程里执行的; - 我并没有在
main
函数里又ExitThread
又std::exit
,std::exit
是在一个独立的线程里执行的;
这整个讨论不就是说detach的错误使用方式,指望detach线程在主线程结束之后的行为是错误设计。
- 这里需要test_exit在test_thread产生顺序关系本身就该用join了。在独立线程里执行的3秒后5秒后你既然说这并不是真的3秒后5秒后,那肯定就是没有顺序关系呗,那这里显然实现了一个data race。
- 我只是打错了,又在main里面ExitThread又std::exit,这并不改变判断,我就是说明ExitThread虽然没以C++形式回收资源,但还是清理了IO等资源的,从其他线程调用std::exit回收资源会显然造成这部分的重复回收。
这整个讨论不就是说detach的错误使用方式,指望detach线程在主线程结束之后的行为是错误设计。
- 这里需要test_exit在test_thread产生顺序关系本身就该用join了。在独立线程里执行的3秒后5秒后你既然说这并不是真的3秒后5秒后,那肯定就是没有顺序关系呗,那这里显然实现了一个data race。
- 我只是打错了,又在main里面ExitThread又std::exit,这并不改变判断,我就是说明ExitThread虽然没以C++形式回收资源,但还是清理了IO等资源的,从其他线程调用std::exit回收资源会显然造成这部分的重复回收。
“指望detach线程在主线程结束之后的行为是错误设计”这个的确,这个目前是共识,很多人使用 detach
的时候说是说分离了不管,其实基本上都是要求它在主线程之前执行完毕的,不然没办法保证资源,程序运行的正确性,前面聊了很多这方面的了。
但是我们目前在说的就是,有没有平台,在主线程乃至当前进程结束之后,被 detach
的线程仍然能正确运行;这也是为什么我前面写的例子要延时,等等,这就是目前的最终问题。
其实还看了一下 _endthreadex 直接标记了这么个玩意儿
For an executable file linked with Libcmt.lib, do not call the Win32 ExitThread API; this prevents the run-time system from reclaiming allocated resources. _endthread and _endthreadex reclaim allocated thread resources and then call ExitThread.
似乎也做不到保证这个资源管理不出现问题
你们不要直接把一整个 comment 都给 quote reply 了,quote 要简明扼要一些的
X _ X
你们不要直接把一整个 comment 都给 quote reply 了,quote 要简明扼要一些的
X _ X
啥意思?