linux 多线程服务端编程读书笔记(一)

linux 多线程服务端编程读书笔记(一)

第一章 线程安全的对象生命期管理

1、class线程安全的三个条件:
  1. 多个线程访问表现出正确的行为

  2. 无论操作系统如何调度,以及线程的执行顺序如何

  3. 调用端代码不需要额外的同步与协调动作

    由此,STL大多数的class都不是线程安全的,需要外部加锁才能同时访问

  4. MutexLock与MutexLockGuard

2、线程安全的对象构造方法
  1. 构造方法

    • 不要在构造函数中注册任何回调
    • 不要在构造函数把this指针传给跨线程的对象,最后一行也不行
  2. 原因

    构造期间对象还没有完成初始化,this泄漏给了别的对象(自身创建的子对象除外),别的线程有可能访问这个半成品对象

  3. 解决方法

    二段式构造:构造函数+initialize

    class Foo:public Observer
    {
    public:
        Foo();
        ...
        void observer(Obeserver* s){
    		s->register(this);
    	}
        
    }
    Foo* pFoo = new Foo;
    Observable* s = getSubject();
    pFoo->observe(s);
        
    
3、 mutex不是办法
  1. 作为数据成员的mutex不能保护析构,因为析构可能销毁mutex本身,而此时析构函数没有完成
  2. 如果同时读写一个class的两个对象,会存在死锁现象,比如swap(a,b),与swap(b,a)在两个线程中同时执行,会发生死锁(对同一个对象同时加锁)
5、 三种对象关系的线程安全性
  1. 组合(composition):很明晰。它们的生命期都由其拥有者控制,是一致的,不会有什么问题。
  2. 关联(association):只有另一个对象的指针或引用,无法知道其是生是死。判断指针是否合法在 C++ 中没有高效的方法。
  3. 聚合(aggregation):其困难和关联类似。虽然是整体和部分的关系,但这个“部分”的生命期受外部影响。
6、Observer模式中的线程安全问题

多个 Observer 的指针(引用)注册到 Observable,这些指针的使用可能引发 race condition

7、C++ 内存问题及其对策
  1. 缓冲区溢出 buffer overrun:使用 vector 、string 或者自己编写的 Buffer class 管理内存。
  2. 空悬指针/野指针:使用 shared_ptr/weak_ptr。
  3. 重复释放 double delete:使用 scoped_ptr,只在对象析构的时候释放一次
  4. 内存泄露 memory leak:使用 scoped_ptr,对象析构的时候自动释放
  5. 不配对的 new[]/delete:把 new[] 替换为 vector/scoped_array。
  6. 内存碎片 memory fragementation。
8、shared_ptr/weak_ptr
  1. shared_ptr采用引用计数的方式控制对象的生命周期
    1. 注意一个shared_ptr的一个拷贝在某个地方长期被持有,其管理的对象就一直没有被析构
    2. 避免拷贝的性能损耗:一个线程只需在最外层函数持有一个实体对象,之后都可以使用 const reference 来使用这个 shared_ptr 对象
  2. weak_ptr不控制对象的生命周期,但是它知道对象是否还活着,如果活着可以提升为shareed_ptr,如果死了,提升失败,返回一个空的share_ptr
  3. 这两个智能指针计数操作在主流平台上是原子操作,比如两个shared_ptr对象实体可以被两个线程同时读取。
  4. 但是它们的线程安全级别与STL容器一样,对象的读写不是线程安全的,即多个线程读取同一个share_ptr对象,需要加锁
9、 析构所在的线程

​ 使用单独的线程做 shared_ptr 管理的对象的析构,避免耗时的析构发生在关键线程里

10、RAII(资源获取即初始化)

​ 每一个明确的资源配置动作(例如 new)都应该在单一语句中执行,并在该语句中立刻将配置获得的资源交给 handle 对象(如 shared_ptr),程序中一般不出现 delete。

11、weak_ptr与shared_ptr是好搭档,可用于对象池与弱回调

​ 弱回调:如果对象还活着,就调用它的成员函数,否则忽略之

// excerpts from http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (giantchen at gmail dot com)

#ifndef MUDUO_BASE_WEAKCALLBACK_H
#define MUDUO_BASE_WEAKCALLBACK_H

#include 
#include 

namespace muduo
{

// A barely usable WeakCallback

template<typename CLASS, typename... ARGS>
class WeakCallback
{
 public:

  WeakCallback(const std::weak_ptr<CLASS>& object,
               const std::function<void (CLASS*, ARGS...)>& function)
    : object_(object), function_(function)
  {
  }

  // Default dtor, copy ctor and assignment are okay

  void operator()(ARGS&&... args) const
  {
    std::shared_ptr<CLASS> ptr(object_.lock());
    if (ptr)
    {
      function_(ptr.get(), std::forward<ARGS>(args)...);
    }
  }

 private:

  std::weak_ptr<CLASS> object_;
  std::function<void (CLASS*, ARGS...)> function_;
};

template<typename CLASS, typename... ARGS>
WeakCallback<CLASS, ARGS...> makeWeakCallback(const std::shared_ptr<CLASS>& object,
                                              void (CLASS::*function)(ARGS...))
{
  return WeakCallback<CLASS, ARGS...>(object, function);
}

template<typename CLASS, typename... ARGS>
WeakCallback<CLASS, ARGS...> makeWeakCallback(const std::shared_ptr<CLASS>& object,
                                              void (CLASS::*function)(ARGS...) const)
{
  return WeakCallback<CLASS, ARGS...>(object, function);
}

}

#endif

12、关于信号与槽的实现

QT中是靠语言扩展的实现,可以一个信号对应多个槽,也可以多个信号对应一个槽。这里的信号与槽仅仅只支持一对多的关系,当然这种关系也可以使用观察者模式来实现(发布订阅),在腾讯面试时遇见过如果是你如何实现信号与槽

#ifndef MUDUO_BASE_SIGNALSLOTTRIVIAL_H
#define MUDUO_BASE_SIGNALSLOTTRIVIAL_H

#include 
#include 

template<typename Signature>
class SignalTrivial;

template <typename RET, typename... ARGS>
class SignalTrivial<RET(ARGS...)>
{
 public:
  typedef std::function<void (ARGS...)> Functor;

  void connect(Functor&& func)
  {
    functors_.push_back(std::forward<Functor>(func));
  }

  void call(ARGS&&... args)
  {
    // gcc 4.6 supports
    //for (const Functor& f: functors_)
    typename std::vector<Functor>::iterator it = functors_.begin();
    for (; it != functors_.end(); ++it)
    {
      (*it)(args...);
    }
  }

 private:
  std::vector<Functor> functors_;
};

#endif // MUDUO_BASE_SIGNALSLOTTRIVIAL_H

参考

1.《 Linux多线程服务端编程,使用muduo网络库》

你可能感兴趣的:(网络编程,muduo)