跳至主要內容

多线程 - 线程创建

LincZero大约 4 分钟

多线程 - 线程创建

方法简概

主线程从main()开始执行,创建的线程也需要有一个初始函数开始运行,当这个初始函数运行完毕时该线程结束

一般情况下当主线程执行完毕则其他子线程也会被强行终止(说的是thread方法,而detach线程方法可令主线程执行完毕后子线程继续执行)


线程创建方法

  • 线程使用(thread对象)
    • thread
    • join
    • detach
    • joinable
  • 其他创建线程的手法
    • 类对象 / 匿名对象
    • Lambda表达式

比较、适用场景

  • 病毒软件多用detach,不然任务管理器kill掉你的主线程,子线程也都废了

基本使用

thread+join 例程

(一般用这种方式)

#include <iostream>
#include <thread>			// 线程头文件

using namespace std;

void mythreadfn()			// 线程的初始函数
{
    cout << "子线程" << endl;
}
    
int main()					// main函数,主线程的初始函数
{
    /* (1) 分叉(创建线程)
     * 创建线程并指定初始函数,此时线程开始执行了
     * 这里的thread是标准库里的类,构造时传入一个可调用对象(这里是函数指针)*/
    std::thread mythread(mythreadfn);
    /* (2) 合并(等子线程和主线程汇合)
     * 阻塞主线程,让主线程等待子线程执行完毕
     * 可以避免主线程在子线程之前执行完 */
    mythread.join();
    
    cout << "主线程" << endl;
    return 0;
}

thread+detach 例程

可以让主线程执行完毕后子线程继续执行

#include <iostream>
#include <thread>			// 线程头文件

using namespace std;

void mythreadfn				// 线程的初始函数
{
    cout << "子线程" << endl;
}
    
int main()					// main函数,主线程的初始函数
{
    /* (1) 分叉(创建线程)
     * 创建线程并指定初始函数,此时线程开始执行了
     * 这里的thread是标准库里的类,构造时传入一个可调用对象(这里是函数指针)*/
    std::thread mythread(mythreadfn);
    /* (2) 分离(分离主线程不和子线程)
     * 可令主线程在子线程之前执行完毕。执行后,主线程和关联的thread对象之间会失去关联
     * 子线程会驻留在后台运行,相当于被C++运行时库接管。线程执行完毕时,由运行时库负责清理该线程相关的资源 */
    mythread.detach();
    
    cout << "主线程" << endl;
    return 0;
}

joinable

功能:判断是否可以成功使用join()或detach()

返回true:都能使用

返回false:都不能使用

使用场景:比如之前join或detach过一遍就会返回false避免重复重复join或detach

线程入口

(1) 函数

略,如上

(2) 类对象 / 匿名对象

#include <iostream>
#include <thread>			// 线程头文件

using namespace std;

class TA
{
public:
	void operator()(int num)	// 线程的初始函数(类成员函数)【注意这里有两个小括号】
    {
        cout << "子线程" << endl;
    }
}
    
int main()					// main函数,主线程的初始函数
{
    Ta ta;					// 类对象
    /* std::thread mythread(Ta()); 				// 这里用匿名对象也是可以的 */
    /* std::thread mythread(std::ref(ta),15);	// 这里可以用引用 */
    std::thread mythread(ta,15);	// 函数、类、Lambda都是可调用对象
    mythread.join();
    
    cout << "主线程" << endl;
    return 0;
}

(3) Lambda函数

#include <iostream>
#include <thread>			// 线程头文件

using namespace std;

int main()					// main函数,主线程的初始函数
{
    auto mylamthread = []{
        cout << "子线程" << endl;
    }
    std::thread mythread(mylamthread);	// 函数、类、Lambda都是可调用对象
    mythread.join();
    
    cout << "主线程" << endl;
    return 0;
}

(4) 成员函数指针

#include <iostream>
#include <thread>			// 线程头文件

using namespace std;

class A						// 要传递的类对象
{
public:
    mutable int m_i;
	A(int a):m_i(a){
        cout<<"【构造函数】"<<endl
            <<"【地址】"<<this<<endl
            <<"【线程id】"<<std::this_thread:get_id()<<endl;
    }
    A(const A &a):m_i{
        cout<<"【复制构造函数】"<<endl
            <<"【地址】"<<this<<endl
            <<"【线程id】"<<std::this_thread:get_id()<<endl;
    }
    ~A(){
        cout<<"【析构函数】"<<endl
            <<"【地址】"<<this<<endl
            <<"【线程id】"<<std::this_thread:get_id()<<endl;
    }
    void thread_work(int num)					// 【成员函数作为线程的初始函数】
    {
        cout << "《子线程》" << endl
        	<<"《线程id》"<<std::this_thread:get_id()<<endl;
    }
}
    
int main()
{
    A aObj(10);
    // 参:成员函数入口、传对象地址(不是传引用而是传地址,防止复制构造,ref()也行)、成员函数的参数
    thread mythread(&A::Obj.thread_work, &aObj, 20);
    mythread.join();
    cout << "《主线程》" << endl
        <<"《线程id》"<<std::this_thread:get_id()<<endl;
    return 0;
}
  • 调试结果
    • 对象执行一次构造函数(主线程)、一次拷贝构造函数(子线程),两侧析构函数(主线程子线程各一次)
    • 成员函数在子线程中输出

多个线程

多个线程可以用同一个线程入口函数

#include <iostream>
#include <thread>			// 线程头文件

using namespace std;

void myprint(const int i)
{
    cout << "《子线程》" << endl
        <<"《线程id》"<<std::this_thread:get_id()<<endl;
}
    
int main()
{
    vector <thread> mythreads;	//多个线程
    /* 创建10个线程,并使用同一个入口函数(线程池有2w多个,系统线程大约有5k个) */
    for(int i=0; i<10; i++)
    {
        mythreads.push_back(thread(myprint, i));
    }
    /* 遍历并join */
    for(auto iter=mythreads.begin(); iter!=mythreads.end(); iter++)
    {
        iter->join();
    }
    cout << "《主线程》" << endl
        <<"《线程id》"<<std::this_thread:get_id()<<endl;
    return 0;
}