多线程编程之C++实现

“你就像一个饿着肚子,渴望吃糖的小女孩,即使面包就在身边也不愿意尝一口。”

 

期待已久的春天终于快来了~

2020.03.09

 

想把多线程同步的实现方法简单总结一下,尽管网上讲并发编程、多线程同步的经典案例消费者生产者模型一大堆,但还是觉得那些不是自己的东西,当自己在实际项目中使用以后,由繁到简,由深到浅,做一个梳理,我觉得对自己来说是一个很有深度的总结,也是一件很值得去做的事情。

 

当进行并发编程,也就是所谓的多线程编程时,最关键的问题之一是注意多线程之间的同步问题,首先需要弄清的是,这个同步不是同时进行的意思,而是协同步调的意思,即按照预定的先后次序进行运行,比如:你说完,我再说。引用一下百科对线程同步的解释:

即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而此时其他线程则处于等待状态。实现线程同步的几种方式:

互斥锁,条件变量,读写锁,信号量,临界区

 

注:

   1..对多线程来说,同步指的是在一定时间内只允许某一个线程访问某个资源,而在此时间内,不允许其他线程访问该资源!

   2.condition_variable条件变量可以用来实现线程同步,它必须与互斥量mutex配合使用。

   3.condition_variable条件变量适用场景:一个线程先对某一条件进行判断, 如果条件不满足则进入等待, 条件满足的时候, 该线程被通知条件满足, 继续执行任务。

  4.在阻塞wait()该线程之前,必须先lock相关联的mutex, 因为假如目标条件未满足,wait()实际上会unlock该mutex, 然后block,在目标条件满足后再重新lock该mutex, 然后返回。

  5.基本上,每个线程的同步互斥控制流程如下:

     A. 进入后加互斥锁 std::unique_lock<std::mutex> lk(mtx); 

     B.判断此时是否能进行读写,能则立刻进行生产或消费,如不能则等待且释放互斥锁,等到能够生产消费时,再加锁进行生产消费操作。操作结束后通知生产者或者消费者,然后进入C。
     C.释放互斥锁 lk.unlock();

 6.条件变量通过运行线程阻塞和等待另一个线程发送信号的方法弥补互斥锁的不足,常常和互斥锁一起使用,使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开响应的互斥锁并等待条件发生变化,一旦其他的某个线程改变了条件变量,它将通知响应的条件变量换线一个或多个正被此条件变量阻塞的线程,这些线程将重新锁定互斥锁并且重新测试条件是否满足

 7.c++17中新加入了scope_lock: 严格基于作用域(scope-based)的锁管理类模板,构造时是否加锁是可选的(不加锁时假定当前线程已经获得锁的所有权),析构时自动释放锁,不必设置unlock(),所有权不可转移,对象生存期内不允许手动加锁和释放锁。

  8.信号量(sem)和互斥锁的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程进入临界区。

 

 

好了,以上就是一个简单的理论部分的总结,下面给出代码示例:

//C++
#include <bits/stdc++.h>
#include <iostream>
#include <thread>
#include <queue>
#include <unistd.h>
#include <condition_variable>

#include <string>
#include <vector>
#include <stdio.h>
#include <chrono>
#include <mutex>
#include <memory>
#include <time.h>

//生产者消费者模型 condition_variable
//wait 阻塞当前线程,直至被唤醒
//wait_for 阻塞当前线程,直至条件变量被唤醒,或到指定时限时长后,
//即可以指定一个时间段,在当前线程收到通知或者指定的时间 rel_time 超时之前,该线程都会处于阻塞状态。
//如果超时返回std::cv_status::timeout。如果收到了其他线程的通知,返回std::cv_status::no_timeout。
//(g_cv_consume.wait_for(lock, std::chrono::seconds(1)) == std::cv_status::timeout)

static std::mutex g_mutex;
static std::condition_variable g_cv_produce;
static std::condition_variable g_cv_consume;

static std::queue<int> g_queue;
static int maxSize = 20;

void consumer(void)
{
    while(true) {
        sleep(1);
        std::unique_lock<std::mutex> lk(g_mutex);
        while(g_queue.size() == 0) {
            g_cv_consume.wait(lk);
        }
        // lambda 
        //g_cv_consume.wait(lk, [] {return g_queue.size() != 0;});

        std::cout << "consumer thread id: " << this_thread::get_id() << std::endl;

        std::cout << "consumer thread g_queue.front(): " << g_queue.front() << std::endl;
        g_queue.pop();
        std::cout << "consumer thread now queue size is: " << g_queue.size() << std::endl;
        g_cv_produce.notify_all();
        //lk.unlock();
    }

}

void producer(void)
{
    while(true) {
        sleep(1);
        std::unique_lock<std::mutex> lk(g_mutex);
        while(g_queue.size() == maxSize) {
            g_cv_produce.wait(lk);
        }
        // lambda
        //g_cv_produce.wait(lk, [] {return g_queue.size() != maxSize; });

        std::cout << "producer thread id: " << this_thread::get_id() << std::endl;
        std::cout << "producer thread g_queue.front(): " << g_queue.front() << std::endl;
        g_queue.push(1);
        std::cout << "producer thread now queue size is: " << g_queue.size() << std::endl;
        g_cv_consume.notify_all();
        //lk.unlock();
    }
}



int main(int argc, char *argv[]) 
{
    std::thread thread_consumer(consumer);
    std::thread thread_producer(producer);

    if (thread_consumer.joinable())
        thread_consumer.join();
    if (thread_producer.joinable())
        thread_producer.join();
    
	return 0;
}

其结果运行如下:



2020,继续加油哦!

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值