C++ 多线程

How to terminate a C++ std::thread?
C++ 11多线程入门 B站
C++11 并发指南

join(): 函数阻塞线程,直到threadfunc()运行结束,回收对应创建线程的资源。如果不阻塞线程,就不能保证线程对象t1在threadfunc()运行期间有效,会引起程序崩溃。

detach(): 调用detach()函数使得线程对象与线程函数分离,这样,线程对象与线程函数就没有联系了,此时的线程是作为后台线程去执行,detach()后就无法再和线程发生联系,也不能通过join()来等待线程执行完毕,线程何时执行完无法控制,它的资源会被init进程回收,所以,通常不采用detach()方法。

std::thread t1(std::bind(&A::threadfunc,&a));: 还可以通过std::bind来创建线程函数。创建一个类A,然后再main函数中将类A中的成员函数绑定到线程对象t1上。

互斥量(锁)的使用,这是一种线程同步机制,在C++11中提供了4中互斥量。

std::mutex;                  //非递归的互斥量
std::timed_mutex;            //带超时的非递归互斥量
std::recursive_mutex;        //递归互斥量
std::recursive_timed_mutex;  //带超时的递归互斥量

从各种互斥量的名字可以看出其具有的特性,在实际开发中,常用就是std::mutex,它就像是一把锁,我们需要做的就是对它进行加锁与解锁。

func()
{
    //加锁
    执行逻辑处理;    //如果该过程抛出异常导致程序退出了,就没法unlock
    //解锁
      
}

func()中再执行逻辑处理中程序因为某些原因退出了,此时就无法unlock()了,这样其他线程也就无法获取std::mutex,造成死锁现象,其实在加锁之前可以通过trylock()尝试一下能不能加锁。实际开发中,通常也不会这样写代码,而是采用lock_guard来控制std::mutex。

条件变量是C++11提供的另外一种线程同步机制,通过判断条件是否满足,决定是否阻塞线程,当线程执行条件满足的时候就会唤醒阻塞的线程,常与std::mutex配合使用,C++11提供了两种条件变量。

1、std::condition_variable,配合std::unique_lockstd::mutex使用,通过wait()函数阻塞线程;

2、std::condition_variable_any,可以和任意带有lock()、unlock()语义的std::mutex搭配使用,比较灵活,但是其效率不及std::condition_variable;

std::unique_lock:C++11提供的 std::unique_lock是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。std::unique_lock比std::lock_guard使用更加灵活,功能更加强大。使用std::unique_lock需要付出更多的时间、性能成本。

下面利用std::mutex与std::condition_variable实现生产者与消费者模式。



std::list<std::shared_ptr<CTask>> g_task;
std::mutex g_mutex;
std::condition_variable g_conv;//生产者线程
void ProdecerFunc()
{
    
    int n_taskId = 0;
    std::shared_ptr<CTask> ptask = nullptr;
    while (true)
    {
        ptask = std::make_shared<CTask >(n_taskId); //创建任务
      
        {
            std::lock_guard<std::mutex> lock(g_mutex);
            g_task.push_back(ptask);
            std::cout << "produce a task Id is " << n_taskId << std::endl;}
        //唤醒线程
        g_conv.notify_one();
​
        n_taskId++;
​
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}//消费者线程
void ConsumerFunc()
{
    std::shared_ptr<CTask> ptask = nullptr; 
    while (true)
    {
        std::unique_lock<std::mutex> lock(g_mutex);
        while (g_task.empty())  //即使被唤醒还要循环判断一次,防止虚假唤醒
        {
            g_conv.wait(lock);
        }
​
        ptask = g_task.front();  //取出任务
        g_task.pop_front();if (ptask == nullptr)
        {
            continue;
        }
        ptask->dotask();       //执行任务
        
    }
}int main()
{
    std::thread t1(ConsumerFunc);
    std::thread t2(ConsumerFunc);
    std::thread t3(ConsumerFunc);
​
    std::thread t4(ProdecerFunc);
​
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    
    return 0;
}

条件变量的使用过程可以归纳如下:

1、拥有条件变量的线消费者程获取互斥锁;
2、消费者线程循环检查条件是否满足,不满足则阻塞等待,此时释放互斥锁;
3、当生产者线程产生任务后,调用notify_one()或者notify_all()唤醒阻塞的消费者线程;
4、当消费者线程被唤醒后再次获得互斥锁去执行任务;

QT多线程
请注意,在构造函数中没有分配任何父函数,并且还确保将其定义为指针。这在我们打算使用moveToThread函数时是至关重要的。有父对象的对象不能移动到新线程中
第二个非常重要的注意点是不应该直接调用VideoProcessor的startVideo函数,而应通过将一个适当的信号连接到它进行调用。
即以这种方式启动线程之后,必须通过调用quit函数来终止该线程,并且在其对象中不应含有任何正在运行的循环或挂起的指令。如果不满足这两个条件中的任意一个,则在处理线程时,会遇到严重的问题。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值