c++线程

使用thread创建线程

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

void fun(int age, string name) {
    cout <<"age ="<<age<<"  name = "<<name<<endl;
}

int main() {
    thread t1(fun, 22, "lisi");
    t1.join(); //回收线程t1的资源
    return 0;
}
image.png

线程资源回收

虽然同一个进程的多尔线程共享进程的栈空间,但是每个子线程在整个栈中拥有自己私有的栈空间,所以线程结束时候需要回收资源

回收资源有两种方法:

  • 在主程序中,调用join()成员函数等待子线程退出,回收它的资源,如果子线程已退出,join()函数立即返回,否则会产生阻塞,知道子线程退出
  • 在子线程中,调用detach()成员寒素,分离子线程,子线程退出时,系统自动回收资源
    用joinable()成员函数可以判断子线程的分离状态,函数返回bool类型

this_thread的全局函数

c11提供了命名空间this_thread 来表示当前线程,该命名空间中有四个函数

  • get_id() 获取线程id
cout << "thread_id = "<<this_thread::get_id()<<endl;
  • sleep_for() 让线程休眠一段时间
// 休眠1秒
this_thread::sleep_for(chrono::seconds(1));
  • sleep_until() 让线程休眠到指定时间点(可实现定时任务)
  • yield() 让线程主动让出自己已经抢到的cpu时间片

call_once函数

在多线程环境中,某些函数只能被调用一次,此时可以用std::call_once()来保证某个函数只被调用一次

#include <iostream>
#include<string>
#include<thread>
#include<mutex> // std::once_flag h和std::call_once需要包含这个头文件
using namespace std;    

once_flag onceflag;

void once_func(const string str) {
    cout << "once_func :"<<str<<endl;
}

void fun(int age, string name) {
    call_once(onceflag, once_func, "我需要在多线程中,只被执行一次");
    cout <<"age ="<<age<<"  name = "<<name<<endl;
}

int main() {
    thread t1(fun, 22, "lisi");
    thread t2(fun, 33, "zhangsan");
    t1.join();
    t2.join();
    return 0;
}

native_handle函数

c11定义了线程标准,不同的平台和编译器在实现的时候本质上都是对操作系统的线程库进行封装,会损失一部分功能

所以thread类提供了native_handle()成员函数,用于获得与操作系统相关的原生线程句柄,操作系统原生的线程库就可以原生线程句柄操作线程。

线程同步

  • 互斥锁
  • 条件变量
  • 生产/消费者模型

互斥锁

mutex

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

mutex mtx;

void print(int id, string name) {
    for (size_t i = 0; i < 10; i++)
    {
        mtx.lock();
        cout <<"我是 "<<id<<"号"<<"  ,我叫:"<<name<<endl;
        mtx.unlock();
        this_thread::sleep_for(chrono::seconds(1));// sleep 1s
    }
    
}

int main() {
    thread t1(print, 11, "lisi");
    thread t2(print, 22, "zhangsan");
    thread t3(print, 33, "lisi");
    thread t4(print, 44, "zhangsan");
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    return 0;
}

**timed_mutex **

timed_mutex tm_mtx;
if (tm_mtx.try_lock()) //try_lock返回true表示加锁成功
{
        /* code */
        tm_mtx.unlock();
}

timed_mutex tm_mtx;
if (tm_mtx.try_lock_for(chrono::seconds(1))) //try_lock_for返回true表示加锁成功
{
    /* code */
    tm_mtx.unlock();
}
    

recursive_mutex

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

class AA {
    private:
        recursive_mutex mtx; //递归锁
    public:
        void fun1() {
            mtx.lock();
            cout <<"fun1 runs.."<<endl;
            mtx.unlock();
        }

        void fun2() {
            mtx.lock();
            cout <<"fun1 runs.."<<endl;
            fun1(); // fun2调用fun1
            mtx.unlock();
        }
};

int main() {
    AA aa;
    aa.fun2();
    return 0;
}

生产者消费者模型

#include <iostream>
#include<string>
#include<thread>
#include<mutex> 
#include<deque>
#include<queue>
#include<condition_variable>
using namespace std;    

class AA {
    mutex m_mutext; //互斥锁
    condition_variable m_cond; // 条件变量
    queue<string, deque<string>> m_q; // 缓存队列,底层容器用deque

    public:
        void incache(int num) { // 生产数据,num指定数据个数
            lock_guard<mutex> lock(m_mutext);//申请加锁
            for (size_t i = 0; i < num; i++)
            {
                static int bh = 1; //物品编号
                string message = to_string(bh++)+"号产品";
                m_q.push(message); // 把生产出来的数据入队
            }
            m_cond.notify_one();//唤醒一个被当前条件变量阻塞的线程
        }

        void outcache() { // 消费者线程任务函数
            string message;
            while (true)
            {
                // 把互斥锁换成unique_lock<mutex>,并申请加锁
                unique_lock<mutex>lock(m_mutext);

                while (m_q.empty())
                {
                    m_cond.wait(lock);
                }

                // 数据元素出队
                message = m_q.front(); 
                m_q.pop();
                cout <<"线程:"<<this_thread::get_id() <<"消费了,"<<message<<endl;
                
                this_thread::sleep_for(chrono::milliseconds(1));//假设数据处理需要1毫秒
            }
            
        }
};

int main() {
    AA aa;

    thread t1(&AA::outcache, &aa); // 创建消费者线程t1
    thread t2(&AA::outcache, &aa); // 创建消费者线程t2
    thread t3(&AA::outcache, &aa); // 创建消费者线程t3

    this_thread::sleep_for(chrono::seconds(2));//休眠2秒
    aa.incache(3); //生产3个数据

    this_thread::sleep_for(chrono::seconds(3));//休眠3秒
    aa.incache(5); // 生产5个数据

    t1.join();
    t2.join();
    t3.join();
    return 0;
}

原子类型atomic

c11提供了atomic<T>模板类,用于支持原子类型,模板参数可以是bool,char,int,long,long long 指针类型(不支持浮点类型和自定义的 数据类型)

#include <iostream>
#include<atomic>
#include<string>
#include<thread>
#include<mutex> 

using namespace std;    

atomic<int>num = 0;

void fun() {
    for (size_t i = 0; i < 1000000; i++)
    {
        num++;
    } 
}

int main() {
    thread t1(fun);
    thread t2(fun);
    t1.join();
    t2.join();

    cout <<"num = "<<num<<endl;
    return 0;
}
int main() {
    atomic<int>a(33);
    cout <<"a = "<<a.load()<<endl;
    a.store(44); //把44存储到原子变量中。
    cout <<"a = "<<a.load()<<endl;

    int old; // 用于存放原值
    old = a.fetch_add(5); // 把原子变量a的值与5相加,返回原值
    cout <<"old = "<<old<<", a = "<<a.load()<<endl;
    old = a.fetch_add(2);
    cout <<"old = "<<old<<", a = "<<a.load()<<endl;
    return 0;
}
image.png
int main() {
    atomic<int>a(33); // 原子变量
    int expected = 3; // 期待值
    int val = 5; // 打算存入原子变量的值
    /**
     * 比较原子变量的值和预期值expected
     * 如果两个值相等,把val存储到原子变量中
     * 如果两个值不相等,用原子变量的值更新预期值
     * 执行存储操作时,返回true,否则返回false
    */
    bool bret = a.compare_exchange_strong(expected, val);
    cout <<"bret = "<<bret<<endl;
    cout <<"a = "<<a<<endl;
    cout <<"expected = "<<expected<<endl;
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,423评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,147评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,019评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,443评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,535评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,798评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,941评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,704评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,152评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,494评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,629评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,295评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,901评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,978评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,333评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,499评论 2 348

推荐阅读更多精彩内容