• muduo笔记 线程同步CountDownLatch


    多线程同步中的问题

    多线程环境中,常有这样一种同步情况:一个线程等待其他所有线程完成指定工作。
    比如,在开启一个新线程后,虽然已经启动线程函数,很可能还有一些准备工作需要完成(如更新当前线程id,名称等信息),这样,调用线程(创建线程的线程)并不能马上投入工作,立即向新线程传递数据,可能造成未定义行为,如调用线程的某段代码依赖于子线程id。

    我们的第一反应,是使用屏障,C++ 20的std::barrier, 或者std::latch。在C++ 20以前,我们只能根据NPTL提供的API,自行封装。

    muduo通过对互斥锁MutexLock + 条件变量Condition的封装,实现CoundDownLatch,进行向下计数,实现线程同步。

    CountDownLatch

    CountDownLatch 也被称为门阀 、 计数器 或者 闭锁。用于多个线程之间的同步,特别是一个线程等待另一个或多个线程。

    CountDownLatch内部持有一个向下计数的计数器count_,构造时给定一个初值,代表需要等待的线程数。每个线程完成一个任务,count_减1,当count_值减到0时,代表所有线程已经完成了所有任务,在CountDownLatch上等待的线程就可以继续执行了。

    CountDownLatch的接口

    需要等待其他线程完成任务的线程,调用wait(),等待count_变为0;
    任务线程,如果完成了任务,就调用countDown(),将count_计数值-1,值减到0时,会唤醒所有等待线程继续执行;

    /**
    * Count down from a count user specified to zero.
    *
    * Thread safe.
    */
    class CountDownLatch
    {
    public:
        explicit CountDownLatch(int count);
    
        /**
         * Wait until count_ decrease to 0
         */
        void wait();
        /**
         * Decrease 1 in count_
         */
        void countDown();
    
        /**
         * Return the value of count_
         */
        int getCount() const;
    
    private:
        mutable MutexLock mutex_;
        Condition cond_ GUARDED_BY(mutex_);
        int count_ GUARDED_BY(mutex_);
    };
    

    CountDownLatch的实现

    通过mutex_,确保所有对count_的操作,都是线程安全的。

    CountDownLatch::CountDownLatch(int count)
    : mutex_(),
      cond_(mutex_),
      count_(count)
    {
    }
    
    void CountDownLatch::wait()
    {
        MutexLockGuard lock(mutex_);
        while (count_ > 0)
        {
            cond_.wait();
        }
    }
    
    /**
    * Count down count_ from init value to 0, then notify thread
    * waiting on the condition cond_.
    */
    void CountDownLatch::countDown()
    {
        MutexLockGuard lock(mutex_);
        --count_;
        if (count_ == 0)
        {
            cond_.notifyAll();
        }
    }
    
    int CountDownLatch::getCount() const {
        MutexLockGuard lock(mutex_);
        return count_;
    }
    

    barrier实现线程同步

    利用NPTL提供的屏障pthread_barrier实现线程同步。
    pthread_barrier的几个接口:

    #include <pthread.h>
    
    int pthread_barrier_destroy(pthread_barrier_t *barrier);
    int pthread_barrier_init(pthread_barrier_t *restrict barrier,
        const pthread_barrierattr_t *restrict attr, unsigned count);
    
    int pthread_barrier_wait(pthread_barrier_t *barrier);
    

    pthread_barrier_init 用于对屏障初始化,pthread_barrier_destroy 用于反初始化。
    初始化时,用count指定要等待的线程数目。attr指定屏障对象的属性,值为NULL表示默认属性初始化屏障。

    调用pthread_barrier_wait,表明线程完成了工作,准备等待所有其他线程赶上来。调用pthread_barrier_wait的线程屏障计数,未达到屏障初始化设置的count初值时,会进入休眠状态。如果线程是最后一个调用pthread_barrier_wait的线程,满足屏障计数,那么所有线程将被唤醒。

    static pthread_barrier_t barrier;
    
    // thread1
    void thread_func1()
    {
        pthread_barrier_init(&barrier, NULL, 2);
        
        std::thread th2(thread_func2);
        th2.detach();
        
        printf("thread1 wait\n");
        pthread_barrier_wait(&barrier);
        sleep(1);
        printf("thread1 run\n");
        pthread_barrier_destroy(&barrier);
    }
    
    // thread2
    void thread_func2()
    {
        printf("thread2 wait\n");
        pthread_barrier_wait(&barrier);
        printf("thread2 run\n");
    }
    

    barrier与自定义CountDownLatch区别

    从屏障提供的接口特性,以及上面的示例,我们可以知道,屏障跟CountDownLatch最大的区别:等待屏障计数的所有线程都会休眠,满足条件后,所有线程都会同时继续运行;而CountDownLatch是一个线程等待另外的一个或多个线程,被等待的线程并不会休眠。

    参考

    https://www.apiref.com/cpp-zh/cpp/thread/latch.html
    https://www.apiref.com/cpp-zh/cpp/thread/barrier.html
    APUE 第3版

  • 相关阅读:
    Android Market google play store帐号注册方法流程 及发布应用注意事项【转载】
    cocos2d-x 调用第三方so文件
    ios cocos2d-x 多点触摸
    linux文件分割(将大的日志文件分割成小的)
    Linux 统计某个字符串出现的次数
    scapy基础-网络数据包结构
    mac 下idea光标问题
    mac ox终端显示 bogon的问题
    hibernate和mybatis的区别
    memcached知识点梳理
  • 原文地址:https://www.cnblogs.com/fortunely/p/15944815.html
Copyright © 2020-2023  润新知