zoukankan      html  css  js  c++  java
  • 创建多个线程、互斥量、死琐

    创建多个线程

    #include<iostream>
    #include<thread>
    #include<vector>
    using namespace std;
    void print(int i)
    {
        cout<<"子线程"<<i<<"开始"<<endl;
        cout<<"子线程"<<i<<"结束"<<endl;
    }
    int main()
    {
        vector<thread>myThreads;
        for(int i=0; i<10; i++)
            myThreads.push_back(thread(print,i));
        for(auto iter = myThreads.begin(); iter!=myThreads.end(); ++iter)
        {
            iter->join();
        }
        cout<<"主线程结束"<<endl;
        return 0;
    }
    

    由下图可以看出多个线程的执行是不确定的,和操作系统对线程的调度有关。
    此处输入图片的描述

    互斥量 多线程保护共享数据

    问题:一个线程往队列内插入数据,另一个线程往队列内取数据,在没有保护措施的情况下,此时的程序是不稳定的。
    措施:采取使用互斥的方法保护共享数据

    #include<iostream>
    #include<thread>
    #include<vector>
    #include<list>
    using namespace std;
    
    class A
    {
    public:
        void input()
        {
            for(int i=0;i<100000;i++)
            {
                cout<<"插入元素"<<i<<endl;
                msg.push_back(i);
            }
        }
    
        void output()
        {
            for(int i=0;i<100000;i++)
            {
                if(msg.empty())
                {
                    cout<<"队列为空"<<endl;
                }
                else
                {
                    int command = msg.front();
                    cout<<"取出元素"<<command<<endl;
                    msg.pop_front();
                }
            }
        }
    
    private:
        list<int>msg;
    };
    
    int main()
    {
        A myobja;
        thread insert_thread(&A::input,&myobja);
        thread pop_thread(&A::output,&myobja);
        insert_thread.join();
        pop_thread.join();
        cout<< "线程执行完成" <<endl;
        return 0;
    }
    
    

    互斥量的用法

    1.lock(),unlook()

    先lock、共享数据、unlock
    成对使用

    void input()
    {
        for(int i=0;i<100000;i++)
        {
            my_mutex.lock();  //加锁
            cout<<"插入元素"<<i<<endl;
            msg.push_back(i);
            my_mutex.unlock();//解锁
        }
    }
    
    void output()
    {
        for(int i=0;i<100000;i++)
        {
            my_mutex.lock();  //加锁
            if(msg.empty())
            {
                cout<<"队列为空"<<endl;
            }
            else
            {
                int command = msg.front();
                cout<<"取出元素"<<command<<endl;
                msg.pop_front();
            }
            my_mutex.unlock();//解锁
        }
    }
    

    2.模板类std::lock_guard

    可取代lock、unlock()

    void output()
    {
        lock_guard<mutex>guard(my_mutex);
        /*
        构造函数里调用look()
        析构函数里调用unlook()
        缺点:不够灵活
        */
        for(int i=0;i<100000;i++)
        {
            if(msg.empty())
                cout<<"队列为空"<<endl;
            else
            {
                int command = msg.front();
                cout<<"取出元素"<<command<<endl;
                msg.pop_front();
            }
        }
    }
    

    死琐

    A等B,B等A或者有一个互等的循环
    产生死锁

    #include<iostream>
    #include<thread>
    #include<vector>
    #include<list>
    #include<mutex>
    using namespace std;
    
    class A
    {
    public:
        void input()
        {
            my_mutex1.lock();
            for(int i=0;i<1000000;i++)
                i=i;
            /*
            其他程序
            */
            my_mutex2.lock();
    
            my_mutex1.unlock();
            my_mutex2.unlock();
        }
    
        void output()
        {
            my_mutex2.lock();
            for(int i=0;i<1000000;i++)
                i=i;
            /*
            其他程序
            */
            my_mutex1.lock();
    
            my_mutex1.unlock();
            my_mutex2.unlock();
        }
    
    private:
        list<int>msg;
        mutex my_mutex1;   ///互斥量1
        mutex my_mutex2;   ///互斥量2
    };
    
    int main()
    {
        A myobja;
        thread insert_thread(&A::input,&myobja);
        thread pop_thread(&A::output,&myobja);
        insert_thread.join();
        pop_thread.join();
        cout<< "线程执行完成" <<endl;
        return 0;
    }
    
    

    此处输入图片的描述
    上面程序进程一把一号锁锁住等待二号锁
    上面程序进程二把二号锁锁住等待一号锁
    产生互相等待

    解决方案,保持调用顺序一致理论上不会死锁

    std::lock()

    一次锁住两个或以上的互斥量
    如果失败则一个都不锁,要么全都锁住。从而避免死琐的问题

    std::lock_guard的std::adopt_lock参数

    lock_guard (mutex_type& m, adopt_lock_t tag);
    lock_guard 对象管理 Mutex 对象 m,与 locking 初始化(1) 不同的是, Mutex 对象 m 已被当前线程锁住。

    lock_guard<mutex>guard(my_mutex1,std::adopt_lock_t);
    

    std::unique_lock

    std::unique_lock 的构造函数的数目相对来说比 std::lock_guard 多,其中一方面也是因为 std::unique_lock 更加灵活,从而在构造 std::unique_lock 对象时可以接受额外的参数。

    参数 功能
    默认构造函数 新创建的 unique_lock 对象不管理任何 Mutex 对象。
    locking 新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞。
    try-locking 新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.try_lock() 对 Mutex 对象进行上锁,但如果上锁不成功,并不会阻塞当前线程。
    deferred 新创建的 unique_lock 对象管理 Mutex 对象 m,但是在初始化的时候并不锁住 Mutex 对象。 m 应该是一个没有当前线程锁住的 Mutex 对象。
    adopting 新创建的 unique_lock 对象管理 Mutex 对象 m, m 应该是一个已经被当前线程锁住的 Mutex 对象。(并且当前新创建的 unique_lock 对象拥有对锁(Lock)的所有权)。
    locking一段时间(duration) 新创建的 unique_lock 对象管理 Mutex 对象 m,并试图通过调用 m.try_lock_for(rel_time) 来锁住 Mutex 对象一段时间(rel_time)。
    locking直到某个时间点(time point) 新创建的 unique_lock 对象管理 Mutex 对象m,并试图通过调用 m.try_lock_until(abs_time) 来在某个时间点(abs_time)之前锁住 Mutex 对象。
    拷贝构造 [被禁用] unique_lock 对象不能被拷贝构造。
    移动(move)构造 新创建的 unique_lock 对象获得了由 x 所管理的 Mutex 对象的所有权(包括当前 Mutex 的状态)。调用 move 构造之后, x 对象如同通过默认构造函数所创建的,就不再管理任何 Mutex 对象了。

    综上所述,由 (2) 和 (5) 创建的 unique_lock 对象通常拥有 Mutex 对象的锁。而通过 (1) 和 (4) 创建的则不会拥有锁。通过 (3),(6) 和 (7) 创建的 unique_lock 对象,则在 lock 成功时获得锁。

  • 相关阅读:
    Mysql存储类型选择
    Mysql的MVCC
    SQL标准中的四种隔离级别
    web权限管理总结
    关于oauth2中为什么不直接返回token而是传授权码code
    课程作业——爬取校园新闻首页的新闻的详情,使用正则表达式,函数抽离
    网络爬虫基础练习
    Python中文词频统计
    关于Maven的配置与学习
    课程作业——综合练习:英文词频统计
  • 原文地址:https://www.cnblogs.com/xcantaloupe/p/10395361.html
Copyright © 2011-2022 走看看