C++多线程(笔记一)

一、一些概念

并发:

有两个或两个以上的任务(独立的活动),同时发生(进行),或者说一个程序同时执行多个独立任务
当cpu的数量少于任务数的时候,其实只是通过轮流调度来实现一种表面的并发进行。
使用并发的原因:可以同时执行多个任务,有提高性能的作用

进程:

可以理解为一个正在执行的可执行程序。

线程:

每个进程都有一个主线程,可以理解为代码的执行通路,我们可以写代码来创建其它线程,就可以实现,在同一个时间,在同一个进程执行多个任务
线程并非越多越好,线程切换也是需要时间的,需要保存恢复各种局部变量。(一般200~300个为佳)

多进程并发与多线程并发

多进程实现并发相比于多线程实现并发而言,它还需要切换虚拟地址空间,开销更大。
多进程之间通信需要借助管道、文件、消息队列、共享内存、socket等技术手段
多线程之间他们是共享一个虚拟地址空间的,即可以共享内存,线程间切换开销更小,但需要解决的一个重要问题就是:要保证数据一致性

以往在不同的开发平台上进行多线程开发都有不同的库、不同的函数,因此编写出来的程序一般不能跨平台。从c++11新标准开始,c++语言本身增加了对多线程的支持,使得编写的程序有了可移植性

二、创建线程的多种方法

  1. thread
    ( 一定会创建线程,如果资源太紧张导致创建不了,程序就会崩溃 )
  2. async
    (也叫创建一个异步任务,但当参数被指定为launch::deferred时,就不会创建线程)(同时相比thread,它一般用于需要返回值的线程)

A.thread创建线程

① 创建一个函数,然后把该函数作为线程的入口地址

void readTheval(int num)
{
    cout << "我是读线程" << this_thread::get_id() ;
    cout << "  我读到的数据是:" << num << endl;
    return;
}
//main函数里面:
    thread th1(readTheval, 5);//创建线程并开始执行
    th1.join();

② 创建一个类,并编写圆括号重载函数,初始化一个该类的对象,把该对象作为线程入口地址

class CircleReLoad
{
public:
    void operator()()
    {
        cout << "创建了一个线程" << this_thread::get_id() << endl;
        /* ...  */
        cout << "线程执行结束!" << endl;
    }
};
//main函数里的:
    CircleReLoad Acase;
    thread th1(Acase);
    th1.join();

③ lambda表达式创建线程

//main函数里
    auto OneThread = [] {
        cout << "lambda表达式创建线程" << endl;
        /* … */
        cout << this_thread::get_id() << "线程执行结束 " << endl;
    };
    thread th1(OneThread);
    th1.join();

④把某个类中的某个函数作为线程的入口地址

class Data_
{
public:
    void GetMsg(){}
    void SaveMsh(){}
};
//main函数里
    Data_ s;
    thread oneobj(&Data_::SaveMsh,&s);
    thread twoobj(&Data_::GetMsg,&s);
    oneobj.join();
    twoobj.join();

多个线程的创建、管理:

可以用vector等容器来存放

void TextThread()
{
     cout << "我是线程" << this_thread::get_id() << endl;
     /*  …  */
     cout << "线程" << this_thread::get_id() << "执行结束" << endl; 
}
 //main函数里     vector threadagg;
     for (int i = 0; i < 10; ++i)
     {
         threadagg.push_back(thread(TextThread));
     }
     for (int i = 0; i < 10; ++i)
     {
         threadagg[i].join();
     }

join(), detach(), joinable()成员函数

join():
加入/汇合,起到了让主线程等待子线程执行完毕的作用,让子线程汇合到主线程。
(一般来讲,主线程都是最后结束的,保证所有子线程都能正常的执行完毕)

detach():
分离,将子线程和主线程分离开来,即子线程就不和主线程汇合了,主线程也不必等待子线程执行完毕
一旦detach,与主线程里的thread对象就会失去对这个子线程的控制,这个子线程就会驻留在后台运行(当此子线程执行完后,由系统自己来清理该线程的资源)
(注意如果用detach,并且该线程使用到一个主线程里的局部变量,比如说子线程中有引用或指针使指向主线程里的变量,那么就会使程序崩溃)
(当传入参数存在隐式转换的时候,也有可能出现一个问题,即主线程已经结束,传入参数的的这个变量已经被回收了,它才开始进行隐式转换并执行构造函数,这样就会引发错误)

(一句话能用join就不要detach)

joinable()
可以判断某thread对象是否还可以join或detach,可以就返回true,否则返回false

B.async创建线程

async是一个函数模板,用来启动异步任务,并返回future对象,future是一个类模板
future对象里边包含线程入口函数所返回的结果(线程返回结果),后续可通过get()成员函数得到返回值。
(当你希望多次调用future的成员函数get时,可改用shared_future类模板,当然这只是将结果存储起来,多次调用的get的值是一样的)

int TextThread(int num)
{
    cout << "我是线程" << this_thread::get_id() << endl;
    /*  …  */
    cout << "线程" << this_thread::get_id() << "执行结束" << endl;
    return num;
}
//main函数里
    future result = async(launch::async,TextThread,6);//
    cout << result.get();

其中:
要是launch::async省略的话,系统会把它当成launch::async | launch::deferred ,即系统会根据当前资源状况自由调度,如果资源不紧张一般会当成launch::async,即会立刻创建一个异步任务并开始执行,反之,则会当成是launch::deferred,会等到你调用成员函数get或wait的时候才由主线程去完成(类似于调用普通函数),当省略该参数时,可以用future_status判断系统最终有无创建线程)

future_status

int GetAVaule(int num)
{
    cout << "我正在执行啊!" << endl;
    cout << "我的ID是:" << this_thread::get_id() << endl;
    Sleep(3000);
    return num;
}
//main函数里
    shared_future result = async(GetAVaule, 55);
    future_status status = result.wait_for(chrono::seconds(4));//等4秒看看能不能执行完(wait_for括号里可直接写4s)
    if (status == future_status::timeout)
    {
        cout << "任务超时,还没执行玩呢" << endl;
    }
    else if (status == future_status::ready)
    {
        cout << "给的时间够用,我已经准备好了" << endl;
        cout << result.get() << endl;
    }
    else if (status == future_status::deferred)
    {
        cout << "如果过你是延迟执行的参数,那就到这来" << endl;
    }

packaged_task

打包任务,把任务包装起来,是一个类模板,是一个用来包装可调用对象的可调用对象

// TextThread为上面那个函数
    packaged_task obj(TextThread);//包装
    thread th1(ref(obj),6);//开始执行线程
    th1.join();
    future result = obj.get_future();
    cout << result.get() << endl;

promise

一个类模板,能够在某个线程中给它赋值,然后可以在其它线程中,把这个值取出来用

void MyThread(promise& pro, int num)
{
    num *= 2;
    pro.set_value(num);
    return;
}
//main函数里
    promise my_prom;
    thread th1(MyThread, ref(my_prom), 66);
    th1.join();
    future result = my_prom.get_future();//future和promise绑定,用于获取线程返回值
    cout << result.get() << endl;

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注