c++线程的一些理解

目前多线程方面我主要用两种方式:Qt的QThread和std::thread,都是跨平台,封装了系统的线程库。 暂时还没用到WindowsApi的thread和Linux的pthread。

线程同步方面主要使用互斥锁mutex和条件变量condition_variable,没有用到一定需要用信号量的场景且互斥锁效率不一定比读写锁差,所以所有项目我都只用互斥锁和条件变量基本都能实现需求,当然目前Qt项目中线程需求过于简单,就不讨论Qt中线程问题。

互斥锁:保证临界区代码同时只能被一个线程访问。最常用的同步方式。

有一些注意的地方Winodows下,锁在同一个线程中还没解锁的情况下再次锁 会直接导致崩溃,据说linux下只是阻塞,有待测试。我们要避免在同一个线程中未解锁的情况下锁两次。其次,mutex最好用raii的方式封装,自动解锁。

条件变量:线程同步中 我们往往需要用条件变量阻塞线程,等到满足条件之后,再唤醒线程。同时提供了等待条件变量满足的 wait 系列方法(wait、wait_for、wait_until 方法),发送条件信号使用 notify 方法(notify_one 和 notify_all 方法),使用 std::condition_variable 对象时需要绑定一个 std::unique_lock 或 std::lock_guard 对象。在linux平台下会存在虚假唤醒的情况,我们可以用while而不用if判断条件规避虚假唤醒。

下面贴个简单的例子:

#include <thread>
#include <mutex>
#include <condition_variable>
#include <list>
#include <iostream>

class Task
{
public:
    Task(int taskID)
    {
        this->taskID = taskID;
    }

    void doTask()
    {
        std::cout << "handle a task, taskID: " << taskID << ", threadID: " << std::this_thread::get_id() << std::endl; 
    }

private:
    int taskID;
};

std::mutex                mymutex;
std::list<Task*>          tasks;
std::condition_variable   mycv;

void* consumer_thread()
{    
    Task* pTask = NULL;
    while (true)
    {
        std::unique_lock<std::mutex> guard(mymutex);
        while (tasks.empty())
        {                
            //如果获得了互斥锁,但是条件不合适的话,pthread_cond_wait会释放锁,不往下执行。
            //当发生变化后,条件合适,pthread_cond_wait将直接获得锁。
            mycv.wait(guard);
        }

        pTask = tasks.front();
        tasks.pop_front();

        if (pTask == NULL)
            continue;

        pTask->doTask();
        delete pTask;
        pTask = NULL;        
    }

    return NULL;
}

void* producer_thread()
{
    int taskID = 0;
    Task* pTask = NULL;

    while (true)
    {
        pTask = new Task(taskID);

        //使用括号减小guard锁的作用范围
        {
            std::lock_guard<std::mutex> guard(mymutex);
            tasks.push_back(pTask);
            std::cout << "produce a task, taskID: " << taskID << ", threadID: " << std::this_thread::get_id() << std::endl; 
        }

        //释放信号量,通知消费者线程
        mycv.notify_one();

        taskID ++;

        //休眠1秒
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    return NULL;
}

int main()
{
    //创建5个消费者线程
    std::thread consumer1(consumer_thread);
    std::thread consumer2(consumer_thread);
    std::thread consumer3(consumer_thread);
    std::thread consumer4(consumer_thread);
    std::thread consumer5(consumer_thread);

    //创建一个生产者线程
    std::thread producer(producer_thread);

    producer.join();
    consumer1.join();
    consumer2.join();
    consumer3.join();
    consumer4.join();
    consumer5.join();

    return 0;
}

2020.9.8

今天面试问道 mutex再加锁情况下,第二个线程再加锁会发生什么行为,我回答阻塞,然后面试官说会挂起,我心想挂起和阻塞不是一回事情吗,回去然后我在网上翻阅,各种五花八门的解释。贴了个貌似能解释通的比喻,我也不知道这比喻合不合理,之后还得看看操作系统导论,大学课程忘光了。

   

首先这些术语都是对于线程来说的。对线程的控制就好比你控制了一个雇工为你干活。你对雇工的控制是通过编程来实现的。

     挂起线程的意思就是你对主动对雇工说:“你睡觉去吧,用着你的时候我主动去叫你,然后接着干活”。

     使线程睡眠的意思就是你主动对雇工说:“你睡觉去吧,某时某刻过来报到,然后接着干活”。

     线程阻塞的意思就是,你突然发现,你的雇工不知道在什么时候没经过你允许,自己睡觉呢,但是你不能怪雇工,肯定你         这个雇主没注意,本来你让雇工扫地,结果扫帚被偷了或被邻居家借去了,你又没让雇工继续干别的活,他就只好睡觉了。至于扫帚回来后,雇工会不会知道,会不会继续干活,你不用担心,雇工一旦发现扫帚回来了,他就会自己去干活的。因为雇工受过良好的培训。这个培训机构就是操作系统。

后来我翻了下操作系统导论,书上只是说了线程的 启动 就绪 阻塞 和死亡状态,都没找到挂起这个词。。。我觉得阻塞状态是对的,一种让出cpu时间分片的操作。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值