首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++11 信号槽 signal/slot

C++11 信号槽 signal/slot

作者头像
用户7886150
修改2021-04-26 17:43:11
修改2021-04-26 17:43:11
1.6K0
举报
文章被收录于专栏:bit哲学院bit哲学院

参考链接: C++ signal()

最近在看陈硕大大 的《Linux 多线程服务端编程:使用 muduo C++ 网络库》  ,看到里面用variadic template 和boost智能指针 实现了一个 signal/slot,现在C++11 已经把 boost的智能指针引入到标准库里边了。就想利用纯C++11 实现一遍。 

结果发现,只要把原来代码中boost智能指针替换为c++11 的智能指针,把陈大大自己实现的MutexLock替换为std::mutex, 

MutexLockGuard 替换为std::lock_guard 就可以了。 

看来陈硕大大在很早以前就把握到了 C++ 的发展趋势啊。 

在贴代码之前 ,先说几点。 

一、 

首先说一下shared_ptr<T>的线程安全性,它的线程安全性级别和std::string是一样的。它的计数操作是原子操作,但是多线程对它的并发读写是不安全的,因为他有两个操作,一个是修改地址一个是修改计数。可以想一下,现在有一个智能指针x指向一片内存,先对它读,比如y=x;,读一半(只修改了y的地址,但是计数还是1),此时再进行写操作,比如x=z,全部执行完,那么x指向z的内存,x原来指的内存因为计数减一被释放,这时再进行y=x读操作的另一半(计数加一),但是内存已经释放了。 

所以多线程读写shared_ptr<T>需要保护临界区。 

二、copyonwrite代替读写锁。 

基本思想就是如果此刻有其他线程正在进行读操作,那么写操作需要在新的副本上执行。 

实际上是这样的,每当进行读操作,则sp(shared_ptr简写)计数加一(计数至少为2)。这时如果有写操作,它先判断计数是否为1,若为1则没有线程读,可以在原内存上修改,若不为1,则复制内容到一片新内存并进行修改。其中写操作全程加锁保证只有一个线程可以写。 

那么我们分析一下,若在写操作时有其他线程要进行读操作会等待锁释放;而没有写操作时可以有很多读操作,在进入和退出读操作的过程中他们的引用计数分别加1、减1,从而保证了读操作时内存的确定以及读操作完成后内存的释放(当然是所有的读操作都完成那么计数为0,自动释放)。又回到开始,有很多读操作时,要执行写操作会开辟新副本。那么读写操作各自管理的两片内存,它们的生命由各自计数管理。 

再说一下,读操作的临界区是很小的,只包括了 s_p本身的读保护,只有一个语句,这个临界区是很小的。而写操作是全程保护的。 

三、看一下画的图。 

sp1就是需要多线程读写保护的智能指针。直接写操作的有clean和add两个函数,直接读操作的只有call函数。wp(wadk_ptr<T>的简称)是槽感知信号生命的指针,在信号中的vector<weak_ptr<slot_imp>>则可以感知每个槽的生命。能感受到对方的生命,就可以执行相应操作。 

不说了,上代码。 

#include<functional>

#include<memory>

#include<vector>

#include<mutex>

#include<assert.h>

#include<iostream>

template<typename Callback>

struct SlotImpl;

template<typename Callback>

struct SignalImpl

{

  typedef std::vector<std::weak_ptr<SlotImpl<Callback> > > SlotList;

  SignalImpl()

    : slots_(new SlotList)

  {

  }

  void copyOnWrite()

  {

    if (!slots_.unique())

    {

      slots_.reset(new SlotList(*slots_));

    }

    assert(slots_.unique());

  }

  void clean()

  {

    std::lock_guard<std::mutex> lock(mutex_);

    copyOnWrite();

    SlotList& list(*slots_);

    typename SlotList::iterator it(list.begin());

    while (it != list.end())

    {

      if (it->expired())

      {

        it = list.erase(it);

      }

      else

      {

        ++it;

      }

    }

  }

  std::mutex  mutex_;

  std::shared_ptr<SlotList> slots_;

};

template<typename Callback>

struct SlotImpl

{

  typedef SignalImpl<Callback> Data;

  SlotImpl(const std::shared_ptr<Data>& data, Callback&& cb)

    : data_(data), cb_(cb), tie_(), tied_(false)

  {

  }

  SlotImpl(const std::shared_ptr<Data>& data, Callback&& cb,

           const std::shared_ptr<void>& tie)

    : data_(data), cb_(cb), tie_(tie), tied_(true)

  {

  }

  ~SlotImpl()

  {

    std::shared_ptr<Data> data(data_.lock());

    if (data)

    {

      data->clean();

    }

  }

  std::weak_ptr<Data> data_;

  Callback cb_;

  std::weak_ptr<void> tie_;

  bool tied_;

};

typedef std::shared_ptr<void> Slot;

template<typename Signature>

class Signal;

template <typename RET, typename... ARGS>

class Signal<RET(ARGS...)>

{

 public:

  typedef std::function<void (ARGS...)> Callback;

  typedef SignalImpl<Callback> SignalImpl_t;

  typedef SlotImpl<Callback> SlotImpl_t;

  Signal()

    : impl_(new SignalImpl_t)

  {

  }

  ~Signal()

  {

  }

  Slot connect(Callback&& func)

  {

    std::shared_ptr<SlotImpl_t> slotImpl(

        new SlotImpl_t(impl_, std::forward<Callback>(func)));

    add(slotImpl);

    return slotImpl;

  }

  Slot connect(Callback&& func, const std::shared_ptr<void>& tie)

  {

    std::shared_ptr<SlotImpl_t> slotImpl(new SlotImpl_t(impl_, func, tie));

    add(slotImpl);

    return slotImpl;

  }

  void call(ARGS&&... args)

  {

    SignalImpl_t& impl(*impl_);

    std::shared_ptr<typename SignalImpl_t::SlotList> slots;

    {

      std::lock_guard<std::mutex> lock(impl.mutex_);

      slots = impl.slots_;

    }

    typename SignalImpl_t::SlotList& s(*slots);

    for (typename SignalImpl_t::SlotList::const_iterator it = s.begin(); it != s.end(); ++it)

    {

      std::shared_ptr<SlotImpl_t> slotImpl = it->lock();

      if (slotImpl)

      {

        std::shared_ptr<void> guard;

        if (slotImpl->tied_)

        {

          guard = slotImpl->tie_.lock();

          if (guard)

          {

            slotImpl->cb_(args...);

          }

        }

        else

        {

          slotImpl->cb_(args...);

        }

      }

    }

  }

 private:

  void add(const std::shared_ptr<SlotImpl_t>& slot)

  {

    SignalImpl_t& impl(*impl_);

    {

      std::lock_guard<std::mutex> lock(impl.mutex_);

      impl.copyOnWrite();

      impl.slots_->push_back(slot);

    }

  }

  const std::shared_ptr<SignalImpl_t> impl_;

};

using namespace std;

void hello()

{

    int a = 0;

    int b = 1;

    cout << "hello" << endl;

}

void print(int i)

{

    cout << "print " << i << endl;

}

void test()

{

    {

        Signal<void(void)> sig;

        Slot slot1 = sig.connect(&hello);

        sig.call();

    }

    Signal<void(int)> sig1;

    Slot slot1 = sig1.connect(&print);

    Slot slot2 = sig1.connect(std::bind(&print, std::placeholders::_1));

    std::function<void(int)> func1(std::bind(&print, std::placeholders::_1));

    Slot slot3 = sig1.connect(std::move(func1));

    {

        Slot slot4 = sig1.connect(std::bind(&print, 666));

        sig1.call(4);

    }

    sig1.call(4);

}

int main()

{

    test();

    char c; cin >> c;

}

下面再贴一下Makefile 

iCXXFLAGS=-g -Wall -rdynamic -march=native

CXXFLAGS+=-O2

TEST=signal_slot_test

$(TEST):signal_slot_test.cpp

$(TEST):

    g++ signal_slot_test.cpp -o signal_slot_test -std=c++11

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档