C++ 线程 第一次使用

人盡茶涼 提交于 2020-01-01 01:20:13

经常会遇到以下场景:

1、在界面程序中执行某个耗时操作:

     界面的UI消息循环运行在某一个单独的线程中,一般是主线程,这个时候如果有一个耗时的操作,比如说是下载,如果也放到UI 线程中去,那么界面线程就会阻塞在下载操作那儿,导致界面卡死,这个时候就要将下载操作放到线程中去。

2、守护线程:

     我们希望监视某件事情是否发生,比如监视某一个服务是否停止,如果停止就将它重新启动,一般情况下会去新开一个进程去做这些事情,我们将它称为守护进程,但是有的时候仅仅只是心跳检测而已(每个一段时间检查一下),开启一个线程不划算(主要指进程间通信),这个时候可以将这个心跳任务放到线程里。

3、加速线程:

      现在的cpu很少有单核单线程的cpu,比如我现在的电脑就是4核8线程的,一个线程最多可以占用1/8cpu,这个时候就可以开8 个线程来加速运算。

这些场景都有一个共同的特点:在同一时刻(不考虑并行并发,指的是看上去是同一时刻)运行多个程序段。

使用C11的线程

从C11开始,C++加入了对线程的支持,意味着可以使用std库编写平台无关的多线程代码了。

第一个线程程序:


#include <thread>
#include "color_print.h"

/** 不带参数的线程回调
*/
void ThreadCallBack()
{
    for (int i = 0; i < 100; ++i)
    {
        ColorPrintf(Red, "ThreadCallBack\r\n");
    }
}

int main()
{
    std::thread td(ThreadCallBack);

    for (int i = 0; i < 100; ++i)
    {
        ColorPrintf(Green, "main\r\n");
    }

    td.join();
    return 1;
}

使用std::thread td(ThreadCallBack1);创建一个线程对象并指定回调函数,需要注意的是该线程类是没有shart这个函数的,也就是说创建好了的时候线程就开始运行了。结果如下:

好了第一个多线程程序就算完成了,看起来也不是很难嘛~~

再次观察std::thread类,(默认构造, 就是不带参数的那种不多讨论,这里只说结论:它不会去创建线程。)发现他的构造函数除了指定回调函数外还可以指定额外参数,这些参数会被传入回调函数中去。比如:


/** 带参数的线程回调
@param [in] pirntCount 循环次数
*/
void ThreadCallBack(int pirntCount)
{
    for (int i = 0; i < pirntCount; ++i)
    {
        ColorPrintf(Red, "ThreadCallBack\r\n");
    }
}

int main()
{
    std::thread td(ThreadCallBack, 10);

    for (int i = 0; i < 100; ++i)
    {
        ColorPrintf(Green, "main\r\n");
    }

    td.join();
    return 1;
}

结果如下:

数一下确实只有10个线程输出,途中方框中的颜色显示不正确放到下一篇讨论。

回调类函数

线程回调函数如果是全局函数或者类的静态函数没有什么意思,如何回调某一个类的成员函数呢?

1、实际上只要将类对象的指针传入即可,比如:

class CallBack
{
public:
    /** 线程中运行
    */
    void ThreadRun()
    {
        ColorPrintf(Green, "ThreadRun\r\n");
    }
};

int main()
{
    // 下面代码原理不多讨论,照着用即可
    // 有兴趣的可以参考https://en.cppreference.com/w/cpp/named_req/Callable
    // 其原理和functional类似
    CallBack* pCallBack = new CallBack();
    std::thread td(&CallBack::ThreadRun, pCallBack);

    td.join();
    delete pCallBack;
    pCallBack = NULL;
    return 1;
}

结果如下:

2、thread构造函数中可以传入参数给回调函数,那么我们很容易想到把类的对象指针传给回调函数,由回调函数间接的调用类的成员函数。比如


#include <thread>

class CallBack
{
public:
    /** 线程中运行
    */
    void ThreadRun()
    {
        ColorPrintf(Green, "ThreadRun\r\n");
    }
};

void ThreadCallBack(CallBack* p)
{
    if (p != nullptr)
    {
        p->ThreadRun();
    }
}

int main()
{
    CallBack* pCallBack = new CallBack();
    std::thread td(ThreadCallBack, pCallBack);

    for (int i = 0; i < 100; ++i)
    {
        ColorPrintf(Green, "main\r\n");
    }

    td.join();
    delete pCallBack;
    pCallBack = NULL;
    return 1;
}

结果和上面的一样,这里不截图了。

需要注意的是要保证pCallBack对象的生命周期要比线程的长,可以使用week_ptr,但是本文不作讨论。

回调类的成员函数这样做也是可以成功的,但是总感觉这样做有些,嗯,不够优雅(在那寒冷的日子里,回调函数一般都是这么干的)?C11支持线程的同时还支持了另外两个东西,一个叫做lambda,一个叫做functional,可以这样使用:

3、对于lambda:


#include <thread>

class CallBack
{
public:
    /** 线程中运行
    */
    void ThreadRun()
    {
        ColorPrintf(Green, "ThreadRun\r\n");
    }
};

int main()
{
    CallBack* pCallBack = new CallBack();
    std::thread td([](CallBack* p) 
        {
            if (p != nullptr)
            {
                p->ThreadRun();
            }
        }, pCallBack);

    for (int i = 0; i < 100; ++i)
    {
        ColorPrintf(Green, "main\r\n");
    }

    td.join();
    delete pCallBack;
    pCallBack = NULL;
    return 1;
}

嗯,这下子舒服多了~~,关于lambda本文不多讨论,结果和上面的一样,这里不截图了。

4、对于functional:


#include <thread>
#include <functional>

class CallBack
{
public:
    /** 线程中运行
    */
    void ThreadRun()
    {
        ColorPrintf(Green, "ThreadRun\r\n");
    }
};

int main()
{
    CallBack* pCallBack = new CallBack();
    std::function<void()> func = std::bind(&CallBack::ThreadRun, pCallBack);
    std::thread td(func);

    for (int i = 0; i < 100; ++i)
    {
        ColorPrintf(Green, "main\r\n");
    }

    td.join();
    delete pCallBack;
    pCallBack = NULL;
    return 1;
}

结果同上,同样的关于functional的用法本文不作讨论。

线程回调类成员函数一般会使用这4中方法。对于后三种方法,实际上和线程回调的关系不大,将C++的类函数传给一个C风格的回调一般都是这么干的。

最后

本文仅仅介绍了线程的最基本的用法,对于同步和安全问题留作以后~~

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!