C++如何做到线程保活?

C++如何开启子线程?

创建一个临时的std::thread 变量的同时就启动了线程

#include <stdio.h>
#include <thread>
using namespace std;
void easy_do_something(){
    printf(__FUNCTION__);
    printf("\n");
}

class background_task {
public:
    void operator()() const {
        easy_do_something();
    }
};

int main(int argc, const char * argv[]) {
    background_task f;
    thread new_thread(f);
    std::thread new_thread { background_task()};
    return 0;
}

此时需要注意一下控制台输出: 报错Thread 1: signal SIGABRT

libc++abi: terminating
easy_do_something
(lldb)

这是因为 你未明确线程的结束方式,是加入式还是分离式(下面介绍)
在xcode控制台使用bt命令展示调用栈信息如下
image

如果 std::thread 对象销毁之前还没有做出决定,程序就会终止
( std::thread 的析构函数会调用 std::terminate())

C++如何正确结束线程?

首先解决这个问题之前首先需要知道std::thread 结束的方式分为两种 ,即前面提到的分离式 和 加入式

什么是加入式呢?

加入式是调用std::thread 的 join() 函数 产生的行为,调用 之后,std::thread 对象就会加入当前执行的线程,同步执行,直到std::thread 对象产生的行为执行完毕之后,才会继续往下执行.代码如下:

/*
 在C++11中,使用std::thread创建线程是一种方便的方法。但是,在一个std::thread对象被销毁时,如果目标线程仍然在运行,那么程序就会崩溃。因此,一般建议在析构函数中调用std::thread::join()方法来等待目标线程的执行结束,以确保程序正常退出。

 如果在析构函数中没有调用std::thread::join()方法,那么当std::thread对象被销毁时,它会调用std::terminate()函数来终止程序。这是因为std::thread析构函数默认会调用std::terminate()函数来终止程序,以确保不会留下未终止的线程。

 因此,为了避免程序崩溃,建议在析构函数中调用std::thread::join()方法,以等待目标线程执行结束。如果不想等待目标线程执行结束,可以使用std::thread::detach()方法将线程分离,使得目标线程可以在后台运行而不影响程序的正常退出。
 */
#include <stdio.h>
#include <thread>

class myThread : public std::thread {
public:
    using std::thread::thread;
    ~myThread(){
        printf("~myThread\n");
    }
};

class Test {

public:
    Test(myThread &thread):_thread(thread){
        
    }
    
    ~Test(){
        if (_thread.joinable()) {
            _thread.join();
        }
        printf("~Test\n");
    }
private:
    myThread &_thread;
};

int main(int argc, const char * argv[]) {
    
    myThread new_thread([]{
        printf("~Begin\n");
        for (int i = 0; i<3; ) {
            i++;
            std::this_thread::sleep_for (std::chrono::seconds(1));
            printf(".");
        }
        printf("\n~End\n");

    });
//    new_thread.detach();
    Test test(new_thread);
    
    printf("~main\n");

    return 0;
}

运行结果

~main
~Begin
...
~End
~Test
~myThread
Program ended with exit code: 0

在加入式结束方式下的行为是是同步行为,必须等到线程运行结束才会继续执行之后的代码

把第93行代码注释打开第92行代码,把结束方式改为detach() 分离式调用,运行结果:

~main
~myThread
~Begin
Program ended with exit code: 0

看到创建的new_thread对象线程内的代码没有输出(并不代表没有执行),我们用IO写入本地看看在分离式是否执行了操作
增加两个行数,放在文章最后,此处提前声明

void createFile(const std::string& filename);
void appendToFile(const std::string& filename, const std::string& text);

//将主程序修改如下
int main(int argc, const char * argv[]) {
   const std::string filename = "/Users/xdf_yanqing/Desktop/chatgpt.txt";
    createFile(filename);
    
    myThread new_thread([filename]{
        printf("~Begin\n");
        for (int i = 0; i<3; i++ ) {
            appendToFile(filename, "1234567\n");
            std::this_thread::sleep_for (std::chrono::seconds(1));
        }
        printf("\n~End\n");

    });
    new_thread.detach();
  //  std::this_thread::sleep_for (std::chrono::seconds(1));
    printf("~main\n");

    return 0;
}

运行结果

File created successfully.
~main
~myThread
Program ended with exit code: 0

此时查看生成的文件并没有写入内容,但是我们还是不能确定,将代码第140行注释打开,主程序休眠1s中,此时再运行

File created successfully.
~Begin
Text appended to file successfully.
~main
~myThread
Program ended with exit code: 0

根据打印结果知道,文件写入成功,查看文件确实如此.
问题来了,为什么分离式会出现这种行为呢?下面介绍一下分离式

什么是分离式?

在C++11中,std::thread可以创建一个新的线程,并且提供了两种线程管理方式:joinable(可加入的)和detached(已分离的)。
默认情况下,std::thread对象是joinable的,即可加入的。这意味着当std::thread对象被销毁时,会自动调用std::thread join()方法等待目标线程执行结束。如果目标线程还在运行,那么程序将会阻塞在这里,直到目标线程执行完毕。
但是,如果我们不希望阻塞程序,也不需要等待目标线程执行结束,那么可以将std::thread对象分离。分离后,std::thread对象就变成了detached(已分离的),此时它就不能再调用std::thread join()方法等待目标线程执行结束了。在这种情况下,目标线程会在后台运行,直到执行结束。
std::thread detach()方法可以将std::thread对象分离,使得目标线程可以在后台运行。它将std::thread对象和目标线程分离开来,并且释放了它们之间的关联。分离后的std::thread对象不再和目标线程同步,也不能再调用std::thread join()方法等待目标线程执行结束了。
需要注意的是,如果一个std::thread对象已经被分离,那么它就不能再加入到其他线程中了。因此,在调用std::thread detach()方法前,需要确保目标线程不再需要等待它执行结束,以免导致资源泄漏和死锁等问题。
总之,std::thread的分离式可以让程序在后台执行任务,从而提高程序的并发性能,但需要注意资源的释放和线程的同步问题。

那么 std::thread detch()函数调用后,std::thread 对象在线程结束前销毁了会发生什么?

如果调用detach()函数后,std::thread对象在其关联的线程结束之前被销毁,会导致未定义行为。这是因为detach()函数会将线程和std::thread对象分离,使得它们可以独立运行。一旦分离完成,std::thread对象就不再与关联的线程有任何联系,其生命周期与线程本身的生命周期也分别独立。

因此,如果std::thread对象在其关联的线程结束之前被销毁,可能会导致以下不可预测的行为:

  1. 程序可能崩溃。
  2. 关联线程可能会被强制结束,导致资源无法正常释放。
  3. 程序可能会在一些平台上运行良好,但在另一些平台上出现错误,因为线程和std::thread对象的实现方式可能会因平台而异。

因此,一般来说,最好不要在std::thread对象销毁之前使用detach()函数。如果想要让线程在后台运行,而不需要等待它的结束,可以考虑使用std::thread对象的joinable()函数来检查线程是否可以被join()。如果线程可以被join(),可以使用join()函数来等待线程的结束。如果线程不需要被等待,可以将std::thread对象设置为std::thread()(默认构造函数)或使用std::move()函数将其转移到另一个对象中。

C++如何管理线程?

主要分为4部分:

  1. std::thread 类:这个类封装了一个单独的执行线程,可以使用它来创建新的线程。该类提供了一组成员函数来控制线程的行为,如 join() 和 detach()。

  2. 线程同步:在多线程编程中,需要考虑多个线程之间的同步问题,以确保线程安全和避免竞态条件。C++ 提供了许多同步机制,如 std::mutex,std::condition_variable,std::atomic 等。

  3. 线程池:在某些情况下,需要管理多个线程以处理任务队列。C++ 11 引入了 std::async 和 std::future,它们可以用来管理线程池并获取异步任务的结果。

  4. 并发数据结构:在多线程编程中,需要使用一些并发数据结构来保证数据的一致性和线程安全性。C++ 11 引入了一些新的容器,如 std::atomic,std::atomic_flag,std::atomic 等。

C++如何做到线程保活?

其实很简单,只需要在线程中增加一个while无限循环,根据需要结束循环也就是结束线程,当然循序过程中也可以向线程中增加任务.

_thread = std::thread(&ThreadTaskHelper::ThreadProc, this);		
void ThreadTaskHelper::ThreadProc()
{
	_tEvent.Notify();
	while (_bRun)
	{
		_tEvent.Wait_For(100);
		_lstThreadTask.ProcessTask([&]() {
			return true;
		});			
	}
}
posted @ 2023-03-17 20:52  严_青  阅读(363)  评论(0编辑  收藏  举报