关于c++:多线程服务器编程2线程同步精要

32次阅读

共计 2629 个字符,预计需要花费 7 分钟才能阅读完成。

线程同步的四项准则

  • 最低限度的共享对象,缩小须要同步的场合
  • 应用高级的并发构件,如 TaskQueue,Producer-Consumer Queue,CountDownLatch 等
  • 非不得已应用底层同步原语时,只应用非递归的互斥器和条件变量,慎用读写锁,不要应用信号量
  • 除了应用 atomic 整数外,不本人编写 lock-free 代码,也不要应用内核级同步原语

互斥器(mutex)

应用准则

  • 应用 RAII 手法封装 mutex 的创立、销毁、加锁、解锁

    • 保障了锁的失效期间等于一个作用域(Scoped Locking), 在死锁产生时有助于定位
    • 保障了不手工调用 lock 和 unlock
    • 保障了不跨过程和线程调用锁
    • 保障了不遗记解锁和反复加锁
  • 只应用非递归的 mutex(即不可重入的 mutex)
  • 每次结构 Guard 对象时,思考一路上曾经持有的锁(函数调用栈),避免因桎梏程序不同导致的死锁

为什么只应用非递归的 mutex

  • 同一个线程能够反复对递归的 mutex 加锁,但不能反复对非递归的 mutex 加锁
  • 在同一个县城里屡次对非递归的 mutex 加锁会导致死锁
  • 两者性能差异不大

起因

  • 非递归的锁能提前暴露出编程问题

死锁

  • 除了线程间的死锁,单个线程也会造成死锁
  • 在保障应用 Scoped Locking 时,能够通过剖析调用栈失去起因

条件变量

应用条件变量实现的 BlockingQueue

muduo::MutexLock mutex;
muduo::Condition cond(mutex);
std::deque<int> queue;

int dequeue(){MutexLockGuard lock(mutex);
    while(queue.empty()){cond.wait(); // unlock mutex and wait (原子地)
    }
    assert(!queue.empty());
    int top = queue.front();
    queue.pop_front();
    return top;
}

void enqueue(int x){
    {MutexLockGuard lock(mutex);
    queue.push_back(x);
    }
    cond.notify(); // wakeup the wait side}
  • 每次退出都调用 notify(),而不是 notifyall()是为了防止惊群效应
  • 不只在 0 ->1 的时候 notify(),而是每加一次告诉一次,是当存在多个消费者时,能高效地唤醒他们,不然只唤醒了一个
  • 详见 https://book.douban.com/annot…

应用条件变量实现 CountDownLatch(倒计时)

class CountDownLatch : boost::noncopyable{
    public:
        explicit CountDownLatch(int count);
        void wait();
        void countDown();
    private:
        mutable MutexLock mutex_;
        Condition condition_;
        int count_;
}

void CountDownLatch::wait(){MutexLockGuard lock(mutex_);
    whilt(count_>0){condition_.wait();
    }
}

void CountDownLatch::countDown(){MutexLockGuard lock(mutex_);
    --count_;
    if(count_ == 0){condition_.notifyAll();
    }
}

封装 MutexLock、MutexLockGuard、Condition 的实现

class MutexLock : boost::noncopyable{
    public:
        MutexLock()
        :holder_(0)
        {pthread_mutex_init(&mutex_NULL);
        }

        ~MutexLock()
        {assert(hoilder_ == 0);
            pthread_mutex_destory(&mutex);
        }

        bool isLockedByThisThread(){return holder_ == CurrentThread::tid();
        }
        
        void assertLocked(){assert(isLockedByThisThread());
        }
        
        void lock(){pthread_mutex_lock(&mutex);
            holder_ = CurrentThread::tid();}

        void unlock(){
            holder_ = 0;
            pthread_mutex_unlock();}

        pthread_mutex_t* getPthreadMutex(){return &mutex;}
    private:
        pthread_mutex_t mutex_;
        pid_t holder_; 
}

class MutexLockGuard : boost::noncopyable{
    public:
        explicit MutexLockGuard(MutexLock& mutex)
        :mutex_(mutex) 
        {mutex_.lock();
        }
        
        ~MutexLockGuard(){mutex_.unlock;}
    private:
        MutexLock& mutex_;
}

#define MutexLockGuard(x) static_assert(false,"Missing mutex guard variable name!")

class Condition : boost::noncopyable{
    public:
        explicit Condition(MutexLock& mutex)
        :mutex_(mutex)
        {pthread_cond_init(&pcond_,NULL);
        }

        ~Condition(){pthread_cond_destory(&pcond_);
        }

        void wait(){pthread_cond_wait(&pcond_,mutex_.getPthreadMutex());
        }

        void notify(){pthread_cond_signal(&pcond_);
        }
        
        void notifyAll(){pthread_cond_boardcast(&pcond);
        }

    private:
        MutexLock& mutex_;
        pthread_cond_t pcond_;
}

小结

  • 线程同步的四项准则:尽量应用高级同步设施
  • 对于 其余 的工作,应用一般互斥器和条件变量,采纳 RAII 手法和 Scooped Locking

正文完
 0