C++多线程

C++11中支持内置的thread库,此库兼容window和linux,在不同的平台都可以编译

实现线程并发

一个进程中可以存在并运行多个线程,其中有一个是主线程,当主线程结束后这个进程就结束了,所以要实现线程的并发,就要在主线程结束之间进行

创建线程的方法

  1. 传递一个普通函数
#include <iostream>
#include <thread>
#include <windows.h>

void func(const std::string &str) {
    for (int i = 0; i < 10; ++i) {
        std::cout << str << "第[ " << i << " ]次执行 " << std::endl;
        Sleep(1000);
    }
    std::cout << str << "线程执行完毕" << std::endl;
}

int main() {
    std::cout << "主线程开始" << std::endl;

    std::thread t1(func, "t1");
    for (int i = 0; i < 10; i++) {
        std::cout << "主线程\n";
        Sleep(1000);
    }
    std::cout << "主线程结束" << std::endl;
    t1.join();
    return 0;
}

  1. 传递一个lambda函数
#include <iostream>
#include <thread>
#include <windows.h>

int main() {
    std::cout << "主线程开始" << std::endl;
    auto f = [](const std::string &str) {
        for (int i = 0; i < 10; ++i) {
            std::cout << str << "第[ " << i << " ]次执行 " << std::endl;
            Sleep(1000);
        }
        std::cout << str << "线程执行完毕" << std::endl;
    };
    std::thread t2(f, "t2");
    for (int i = 0; i < 10; i++) {
        std::cout << "主线程\n";
        Sleep(1000);
    }
    std::cout << "主线程结束" << std::endl;
    t2.join();
    return 0;

}

  1. 传递一个仿函数
#include <iostream>
#include <thread>
#include <windows.h>
class MyThread {
public:
	// 仿函数的书写要重写其()运算符
    void operator()(const std::string &str) {
        for (int i = 0; i < 10; ++i) {
            std::cout << str << "第[ " << i << " ]次执行 " << std::endl;
            Sleep(1000);
        }
        std::cout << str << "线程执行完毕" << std::endl;
    };
};

int main() {
    std::cout << "主线程开始" << std::endl;
    std::thread t3(MyThread(), "t3");
    for (int i = 0; i < 10; i++) {
        std::cout << "主线程\n";
        Sleep(1000);
    }
    std::cout << "主线程结束" << std::endl;
    t3.join();
    return 0;

}

  1. 传递一个类的静态成员函数
#include <iostream>
#include <thread>
#include <windows.h>
class Mythread2 {
public:
    static void func(const std::string &str) {
        for (int i = 0; i < 10; ++i) {
            std::cout << str << "第[ " << i << " ]次执行 " << std::endl;
            Sleep(1000);
        }
        std::cout << str << "线程执行完毕" << std::endl;
    }
};

int main() {
    std::cout << "主线程开始" << std::endl;
    std::thread t4(Mythread2::func, "t4");
    for (int i = 0; i < 10; i++) {
        std::cout << "主线程\n";
        Sleep(1000);
    }
    std::cout << "主线程结束" << std::endl;
    t4.join();
    return 0;
}

  1. 传递一个类的成员函数
#include <iostream>
#include <thread>
#include <windows.h>
class Mythread3 {
public:
    void func(const std::string &str) {
        for (int i = 0; i < 10; ++i) {
            std::cout << str << "第[ " << i << " ]次执行 " << std::endl;
            Sleep(1000);
        }
        std::cout << str << "线程执行完毕" << std::endl;
    }
};

int main() {
    std::cout << "主线程开始" << std::endl;
    Mythread3 thread3;
    std::thread t5(&Mythread3::func, &thread3, "t5");
    for (int i = 0; i < 10; i++) {
        std::cout << "主线程\n";
        Sleep(1000);
    }
    std::cout << "主线程结束" << std::endl;
    t5.join();
    return 0;

}

thread.join

上面创建线程如果不添加join,程序就有可能报错

原因就是,主线程和子线程不能确定谁先执行完。两个线程无论是哪个先执行完毕都导致程序异常终止。
因为主线程资源会在程序结束后被回收,而子线程不会被回收。
对于多线程程序中的线程资源一定要被回收,不然就会报错

线程执行join的作用是
将线程加入到线程队列中之前,主线程要等待子线程执行完毕之后才会执行

#include <iostream>
#include<thread>
using namespace std;



void myPrint()
{
    for(int i=0; i<10; i++)
    {
        cout << "线程正在执行" <<i<<endl;
    }
}
int main()
{

    thread firstThread(myPrint);
    firstThread.join();
    for(int j =0;j<5;j++)
    {
        cout<<"主线程正在执行"<<j<<endl;
    }
    return 0;
}



输出结果:
在这里插入图片描述

  • 如果去除了join方法,程序就会发生难以预测的错误
    在这里插入图片描述

thread.detch()

子线程可以脱离主线程,两者并发执行,当主线程执行完毕之后,子线程将会被移动到C++运行时库管理,也就时放到了后台执行,这样不会时程序发生错误,但是子程序执行的结果不会出现在控制台

#include <iostream>
#include<thread>
using namespace std;



void myPrint()
{
    for(int i=0; i<10000; i++)
    {
        cout << "线程正在执行" <<i<<endl;
    }
}
int main()
{

    thread firstThread(myPrint);
//    firstThread.join();
    firstThread.detach();
    for(int j =0;j<5;j++)
    {
        cout<<"主线程正在执行"<<j<<endl;
    }
    return 0;
}


执行结果:
在这里插入图片描述

  • 在子线程执行到803次循环时,主线程就结束了,所以后面的循环在控制台没有输出,这并不代表程序会在这里中断,说不定下次就能执行1000多次循环,这是未知的。
  • (但是可以知道的是main函数中的循环结束后程序依然在执行,说明程序没有在此中断。
  • 最后也没有因为主线程的结束而出现程序的错误,说明detach确实将子线程移动到了后台运行。

thread.joinable()

判断子线程能否加入到线程队列中

  • 一般当线程执行join后不能再次执行join
  • 在线程执行detach后不能再次执行join
#include <iostream>
#include<thread>
using namespace std;



void myPrint()
{
    for(int i=0; i<10; i++)
    {
        cout << "线程正在执行" <<i<<endl;
    }
}
int main()
{

    thread firstThread(myPrint);

    if(firstThread.joinable())
    {
        cout<<"子线程可以join"<<endl;
        firstThread.join();
    }
    else
    {
        cout<<"子线程不能join"<<endl;
    }
    if(firstThread.joinable())
    {
        cout<<"子线程可以join"<<endl;
        firstThread.join();
    }
    else
    {
        cout<<"子线程不能join"<<endl;
    }
//    firstThread.join();
//    firstThread.detach();
    for(int j =0;j<5;j++)
    {
        cout<<"主线程正在执行"<<j<<endl;
    }
    return 0;
}


执行结果:
在这里插入图片描述

pthread_cancle()

在window下的thread中无法对其进行操作
但在linux下的pthread库可以操作将子线程在主线程的运行状态下中断

#include <iostream>
#include <thread>
#include<pthread>

int main() {
    std::cout << "主线程开始" << std::endl;
    auto f = [](const std::string &str) {
        for (int i = 0; i < 10; ++i) {
            std::cout << str << "第[ " << i << " ]次执行 " << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(5));
        }
        std::cout << str << "线程执行完毕" << std::endl;
    };
	thread t5(f, "t5");
    std::this_thread::sleep_for(std::chrono::seconds(5));
    pthread_t thid = t5.native_handle();
    pthread_cancel(thid);
	t5.join();
    return 0;
}

因为子线程等待的时间是10秒,主线程等待的时间是5秒,按照常理子线程整个程序会等待10秒后停止执行
主线程可以通过使用pthread_cancle来终止子线程的执行,要传递的是子线程的id(注意:这里的id不能通过get_id来获取,这里的id指的是pthread_id,只能通过native_handle()函数来获取

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值