笔记_线程参数_多线程执行_互斥锁_死锁_unique_lock_lock_guard

传递类对象,智能指针作为线程参数

class TA
{
public:
     int cc;
     TA(int a) :cc(a) { cout << "构造函数执行: " << this << " threadId: " << std::this_thread::get_id << endl; };
     TA(const TA& a):cc(a.cc){ cout << "拷贝构造函数执行: " << this << " threadId: " << std::this_thread::get_id << endl; }
};
void mprint2( TA &aa)
{
    aa.cc = 100;
    cout << "子线程参数地址是: " << &aa << " threadId: " << std::this_thread::get_id << endl;
}
int main()
{
    TA tt(10);
    std::thread myObj(mprint2, std::ref(tt));
    myObj.join();
}


使用std::ref,传递真正的类引用过去

智能指针作为参数传递

void mprint3(unique_ptr<int> cc)
{
    cout << "子线程参数地址是: " << cc << " threadId: " << std::this_thread::get_id << endl;
}
int main()
{
    unique_ptr<int> cc2(new int(100));
    thread xx(mprint3, std::move(cc2));
    xx.join();
    return 0;
}

类成员函数作为参数传递

class Mc
{
public:
    void thread_fun(int a)
    {
        cout << "Send:" << a << endl;
    }
};
int main()
{
    Mc dd;
    std::thread myobj(&Mc::thread_fun,dd,23);
    myobj.join();
    return 0;
}

多个线程执行

class Mc
{
public:
    void thread_fun(int a)
    {
        cout << "Send:" << a << endl;

        cout << "End:" << a << endl;
    }
};
int main()
{
    Mc TempMc;
    vector <thread> mythreads;
    for (int i = 0;i<10;i++)
    {
        mythreads.push_back(thread(&Mc::thread_fun, TempMc, i));
    }
    for (auto i  = mythreads.begin();i!=mythreads.end();i++)
    {
        i->join();
    }
    cout << "结束执行" << endl;
    return 0;
}
此处执行结果虽然创建顺序又先后,但是执行不一定
主线程等待所有子线程结束运行,最后主线程结束


数据共享问题分析

只读数据

vector <int> g_v = { 1,2,3 };
class Mc
{
public:
    void thread_fun(int a)
    {
        cout << "数据:" << g_v[1]<<g_v[2]<<g_v[0] << endl;
    }
};
int main()
{
    Mc TempMc;
    vector <thread> mythreads;
    for (int i = 0;i<10;i++)
    {
        mythreads.push_back(thread(&Mc::thread_fun, TempMc, i));
    }
    for (auto i  = mythreads.begin();i!=mythreads.end();i++)
    {
        i->join();
    }
    cout << "结束执行" << endl;
    return 0;
}
只读没什么问题


但是既要读又要写就会出问题,如下

class RecvA
{
public:
    void inMsgRecvQueue()
    {
        for (int i = 0;i<100000;i++)
        {
            msgRecvQueue.push_back(i);
            cout << "插入元素" << i << endl;
        }
    }

    void outMsgRecvQueue()
    {
        for (int i = 0; i < 100000; i++)
        {
            if (!msgRecvQueue.empty())
            {
                int commond = msgRecvQueue.front();//先进先出
                msgRecvQueue.pop_front();//移除掉第一个已经取出的元素
                //....处理数据
            }
            else
            {
                cout << "执行,但队列" << i<< "为空" << endl;
            }
        }
        cout << "OutEnd!!!!" << endl;
    }
private:
    std::list<int> msgRecvQueue;//接收的信息
};

int main()
{
    RecvA myobj;
    std::thread outRec(&RecvA::outMsgRecvQueue, &myobj);
    std::thread inRec(&RecvA::inMsgRecvQueue, &myobj);
    outRec.join();
    inRec.join();
    return 0;
}

所以需要给线程加锁,保证一个写的时候,另一个不能读。

互斥量(mutex)

理解为一把锁,多个线程尝试用lock()成员函数来加锁,只有一个线程能锁定成功,等待这个锁定解除了,才能继续上锁。
需要保护的数据一定要加锁,不能遗漏。
lock()与 unlock()要成对使用,否则程序会报异常,而且,不允许锁或者解锁多次。
修复上面的代码

class RecvA
{
public:
    void inMsgRecvQueue()
    {
        for (int i = 0;i<100000;i++)
        {
            my_mutex.lock();
            msgRecvQueue.push_back(i);
            my_mutex.unlock();
            cout << "插入元素" << i << endl;
        }
    }
    bool outmsg(int &command)
    {
        my_mutex.lock();
        if (!msgRecvQueue.empty())
        {
            int commond = msgRecvQueue.front();//先进先出
            msgRecvQueue.pop_front();//移除掉第一个已经取出的元素
            //....处理数据
            my_mutex.unlock();
            return true;
        }
        my_mutex.unlock();
        return false;
    }
    void outMsgRecvQueue()
    {
        int commond = 0;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outmsg(commond);
            if (result)
            {
                cout << "执行成功:" << commond <<endl;
            }
            else
            {
                cout << "执行,但队列" << i<< "为空" << endl;
            }
        }
        cout << "OutEnd!!!!" << endl;
    }
private:
    std::list<int> msgRecvQueue;//接收的信息
    std::mutex my_mutex;
};

int main()
{
    RecvA myobj;
    std::thread outRec(&RecvA::outMsgRecvQueue, &myobj);
    std::thread inRec(&RecvA::inMsgRecvQueue, &myobj);
    outRec.join();
    inRec.join();
    return 0;
}


稳定执行


重点:在ifelse中,对应unlock不能遗漏,少些就会报错
所以引入了一个std::lock_guard的类模板,帮程序员unlock()
注意,这个不能与lock通用

原理:在构造函数里执行了lock,在析构时候unlock;
代码如下

class RecvA
{
public:
    void inMsgRecvQueue()
    {
        for (int i = 0;i<100000;i++)
        {
            std::lock_guard<mutex> sdguard(my_mutex);
            msgRecvQueue.push_back(i);
            cout << "插入元素" << i << endl;
        }
    }

    bool outmsg(int &command2)
    {
        std::lock_guard<mutex> sdguard(my_mutex);
        if (!msgRecvQueue.empty())
        {
            command2 = msgRecvQueue.front();//先进先出
            msgRecvQueue.pop_front();//移除掉第一个已经取出的元素
            //....处理数据
            return true;
        }
        return false;
    }
    void outMsgRecvQueue()
    {
        int commond = 0;
        for (int i = 0; i < 100000; i++)
        {
            bool result = outmsg(commond);
            if (result)
            {
                cout << "执行成功:" << commond <<endl;
            }
            else
            {
                cout << "执行,但队列" << i<< "为空" << endl;
            }
        }
        cout << "OutEnd!!!!" << endl;
    }
private:
    std::list<int> msgRecvQueue;//接收的信息
    std::mutex my_mutex;
    
};

int main()
{
    RecvA myobj;
    std::thread outRec(&RecvA::outMsgRecvQueue, &myobj);
    std::thread inRec(&RecvA::inMsgRecvQueue, &myobj);
    inRec.join();
    outRec.join();
    return 0;
}

死锁问题

两把锁,A,B
一个先锁A,再锁B,另一个先锁B,再锁A,就会出现死锁现象
保证两个上锁顺序一致即可解决问题,解锁顺序对程序无影响
可以用std::lock(my_mutex,my_mutex2);
这样其中一个锁住,另一个锁不住的话,会把之前的也解锁,也能避免死锁问题


adopt_lock

std::lock_guard<mutex> sdguard(my_mutex,std::adopt_lock);


表示已经lock过了,不需要在这个构造里再次lock了,只需要执行析构的unlock即可
所以可以使用两个锁,加两个带adopt_lock标识的方法来解决死锁

unique_lock 

取代lock_guard,比lock_guard灵活很多,但是内存占用要高一些
支持的参数:


1.adopt_lock

同lock_guard,起一个标记作用

chrono::milliseconds dure(20000);//20000毫秒
this_thread::sleep_for(dure);//休息一定时长(dure)
//假设A方法中执行了这段代码,等待20s,并且挂了个锁,那其他线程只能等着这个。

2.try_to_lock


尝试用mutex的lock()去锁定这个mutex,但是若没锁定成功,也会立刻返回,不会阻塞到这里;
用这个的前提是不能先去lock,否则会造成多个锁的错误。
  

  for (int i = 0;i<100000;i++)
        {
            std::unique_lock<mutex> sdguard(my_mutex,try_to_lock);
            if (sdguard.owns_lock())//判断有没有拿到锁
            {
                msgRecvQueue.push_back(i);
                cout << "插入元素" << i << endl;
            }
            else
            {
                //...
            }
        }


3.defer_lock


不能自己先lock,否则会报错
作用是,没有给mutex加锁,初始化了一个没有加锁的mutex。

unique_lock成员函数


1.lock 能控制自己加锁解锁
2.unlock 前面有lock的话,写了也没问题,虽然lock可以自己解锁
3.try_lock();同上
4.release 返回它管理的mutex对象指针,并且释放所有权,也就是说,unique_lock与mutex没有关系了。

unique_lock移动所有权

      

  std::unique_lock<mutex> sdguard(my_mutex);

        std::unique_lock<mutex> sdguard2(std::move(sdguard));


class RecvA
{
public:
    std::unique_lock<mutex> xx()
    {
        std::unique_lock<mutex> tempGuard(my_mutex);
        return tempGuard;
    }
}
int main()
{
    RecvA a;
    std::unique_lock<mutex> xx = a.xx();
    return 0;
}


    
总结:
std::move可以所有权转移
return也可以进行转移

猜你喜欢

转载自blog.csdn.net/qq_35337794/article/details/121658753
今日推荐