muduo源码分析1——CountDownLatch的使用(以及ptr_vector与vector<shared_ptr>性能分析)

1. CountDownLatch是什么

分析muduo::Thread时,看到Thread::start函数,如下所示,其作用为启动线程(关于muduo thread相关的内容,下次写个博客详细分析)

  CountDownLatch latch_;

void Thread::start()
{
  assert(!started_);
  started_ = true;
  // FIXME: move(func_)
  detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);
  if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
  {
    started_ = false;
    delete data; // or no delete?
    LOG_SYSFATAL << "Failed in pthread_create";
  }
  else
  {
    latch_.wait();
    assert(tid_ > 0);
  }
}
  1. 传入Thread内部变量 CountDownLatch latch_,调用pthread_create,创建线程函数startThread,入参为ThreadData* data,指向含有latch_的对象。
  2. startThread函数调用runInThread,其先获得tid(线程对应的实际进程的id),然后调用countDown,再执行实际的线程函数func_
  3. countDown中count–,再调用notifyAll-----pthread_cond_broadcast(&pcond_)
  4. 主线程中wait,调用的是pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()),等到cond,将线程从wait queue放到就绪队列,开始执行

CountDownLatch类定义

class CountDownLatch : noncopyable
{
 public:

  explicit CountDownLatch(int count);

  void wait();

  void countDown();

  int getCount() const;

 private:
  mutable MutexLock mutex_;
  Condition condition_ GUARDED_BY(mutex_);
  int count_ GUARDED_BY(mutex_);
};

runInThread函数

 void runInThread()
  {
    *tid_ = muduo::CurrentThread::tid();
    tid_ = NULL;
    latch_->countDown();
    latch_ = NULL;

    muduo::CurrentThread::t_threadName = name_.empty() ? "muduoThread" : name_.c_str();
    ::prctl(PR_SET_NAME, muduo::CurrentThread::t_threadName);
    try
    {
      func_();
      muduo::CurrentThread::t_threadName = "finished";
    }

写到这里本来没毛病,但是我发现 Thread里写死了latch_(1),所以没有搞明白为什么要搞个count_,后来才知道CountDownLatch不止可以用在Thread里,还可以用在别的地方,比如主线程与多个子线程之间的同步。

  1. count = 3
  2. 主线程中wait
  3. 子线程countdown

2. CountDownLatch的使用

2.1 错误代码分析

为了实现上述线程同步,写了如下代码

#include "muduo/base/CountDownLatch.h"
#include "muduo/base/Mutex.h"
#include "muduo/base/Thread.h"
#include "muduo/base/Timestamp.h"
#include <boost/ptr_container/ptr_vector.hpp> 
#include <unistd.h>
#include <algorithm>
#include <vector>
#include <stdio.h>
#include <memory>
#include <iostream>
using namespace muduo;
using namespace std;

class Test: public enable_shared_from_this<Test>
{
	public:
		Test(int threadNum);
		void mainThFun();
		void joinall();
	private:
		void threadFunc();
		std::vector<std::shared_ptr<Thread>> threads_;
		//boost::ptr_vector<Thread> threads_;
		//std::vector<Thread*> threads_;
		CountDownLatch latch_;
};

Test::Test(int threadNum):latch_(threadNum),threads_(threadNum)
{

	for(int i = 0; i< threadNum ; i++)
	{
		threads_.emplace_back(new Thread(bind(&Test::threadFunc,shared_from_this())));
		//threads_.push_back(new Thread(bind(&Test::threadFunc,this)));
	}

	//for_each(threads_.begin(),threads_.end(),[](std::unique_ptr<Thread> ut){ut->start();});
	for_each(threads_.begin(),threads_.end(),bind(&Thread::start,placeholders::_1));


}

void Test::threadFunc()
{
	sleep(2);
	latch_.countDown();
	cout<<"threadFunc is :"<<CurrentThread::tid()<<endl;
}

void Test::mainThFun()
{
	latch_.wait();
	cout<<"mainThFun : "<<CurrentThread::tid()<<endl;

}
void Test::joinall()
{
	for_each(threads_.begin(),threads_.end(),bind(&Thread::join,placeholders::_1));
	cout<<"joinall"<<endl;
}
int main()
{
	//shared_ptr<Test> testcount = make_shared<Test>(3); 不能=指针
	shared_ptr<Test> testcount(new Test(3));
	//Test testcount(3);
	cout<<"let's go!"<<CurrentThread::tid()<<endl;

	testcount.mainThFun();
	testcount.joinall();

	return 0;
}

CMAKE_MINIMUM_REQUIRED(VERSION 3.10)

PROJECT(threaddemo)

include_directories(/data/lxz/build/release-install-cpp11/include)

link_directories(/data/lxz/build/release-install-cpp11/lib)

add_executable(thread_test Thread.cpp)

target_link_libraries(thread_test muduo_base pthread)

# add_test(NAME exception_test COMMAND exception_test)

一直运行出错,后来发现有两个问题

  1. 关于shared_from_this的使用,shared_from_this()不能用在构造函数里,因为其需要new 一个对象,但是这个对象构造是要先运行构造函数,所以存在先后问题,会导致出错
  2. 对于vector的初始化使用有误,threads_(threadNum),完成初始化后,再调用push_back,会在threadNum个元素之后插入(threadNum=3, 0-0-0-x),导致for_each使用时候使用了前三个nullptr调用start,出现段错误。

修改程序如下,使用vector.reserve,然后在成员函数doit中完成逻辑,但是依然很丑陋,可以使用ptr_vector进行优化

#include "muduo/base/CountDownLatch.h"
#include "muduo/base/Mutex.h"
#include "muduo/base/Thread.h"
#include "muduo/base/Timestamp.h"
#include <boost/ptr_container/ptr_vector.hpp> 
#include <unistd.h>
#include <algorithm>
#include <vector>
#include <stdio.h>
#include <memory>
#include <iostream>
using namespace muduo;
using namespace std;

 template<typename _InputIterator, typename _Function>
    _Function
    for_eacha(_InputIterator __first, _InputIterator __last, _Function __f)
    {
      // concept requirements
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      __glibcxx_requires_valid_range(__first, __last);
      for (; __first != __last; ++__first)
	{
        __f(*__first);}
      return __f; // N.B. [alg.foreach] says std::move(f) but it's redundant.
    }

class Test: public enable_shared_from_this<Test>
{
	public:
		Test(int threadNum);
		void mainThFun();
		void joinall();
		void doit();
	private:
		int num;
		void threadFunc();
		std::vector<std::shared_ptr<Thread>> threads_;
		//boost::ptr_vector<Thread> threads_;
		//std::vector<Thread*> threads_;
		CountDownLatch latch_;
};

Test::Test(int threadNum):latch_(threadNum),num(threadNum)
{
	threads_.reserve(num);

}

void Test::doit()
{
		for(int i = 0; i< num ; i++)
	{
		threads_.emplace_back(new Thread(bind(&Test::threadFunc,shared_from_this()))); //这里不能使用,
        //因为这是在构造函数,shared_ptr时,需要先调用构造函数
		//threads_.push_back(new Thread(bind(&Test::threadFunc,this)));
		//threads_.emplace_back(new Thread(bind(&Test::threadFunc,this)));
	}
        for_eacha(threads_.begin(),threads_.end(), [](shared_ptr<Thread> sh){ sh->start();});
}

void Test::threadFunc()
{
	sleep(2);
	latch_.countDown();
	cout<<"threadFunc is :"<<CurrentThread::tid()<<endl;
}

void Test::mainThFun()
{
	latch_.wait();
	cout<<"mainThFun : "<<CurrentThread::tid()<<endl;

}
void Test::joinall()
{
	for_each(threads_.begin(),threads_.end(),bind(&Thread::join,placeholders::_1));
	cout<<"joinall"<<endl;
}
int main()
{
	//shared_ptr<Test> testcount = make_shared<Test>(3); 不能=指针
	shared_ptr<Test> testcount(new Test(3));
	//Test testcount(3);
	cout<<"let's go!"<<CurrentThread::tid()<<endl;
	testcount->doit();
	testcount->mainThFun();
	testcount->joinall();

	return 0;
}


运行成功,如下

let's go!4276
threadFunc is :4279
threadFunc is :4278
mainThFun : 4276
threadFunc is :4277
joinall

3. ptr_vector的使用

3.1 ptr_vector

ptr_(container)用于以异常安全的方式以最小的开销保存堆对象的容器
相比一般的例如vector< shared_ptr< T>>的优势

  1. 开销非常小
  2. 使用更方便
  3. 异常安全的指针存储和操作
  4. 比一般的智能指针的容器更快
  5. 可用于既不可分配也不可拷贝构造的类型

存储对象是不共享的,专有的
缺点是不如vector< shared_ptr< T>>等容器灵活

ptr_vector的源码如下


    template
    < 
        class T, 
        class CloneAllocator = heap_clone_allocator,
        class Allocator      = std::allocator<void*>
    >
    class ptr_vector : public 
        ptr_sequence_adapter< T, 
                              std::vector<void*,Allocator>, 
                              CloneAllocator >
    {  
        typedef ptr_sequence_adapter< T, 
                                      std::vector<void*,Allocator>, 
                                      CloneAllocator > 
            base_class;

        typedef ptr_vector<T,CloneAllocator,Allocator> this_type;
        
    public:

        BOOST_PTR_CONTAINER_DEFINE_SEQEUENCE_MEMBERS( ptr_vector, 
                                                      base_class,
                                                      this_type )
        
        explicit ptr_vector( size_type n,
                             const allocator_type& alloc = allocator_type() )
          : base_class(alloc)
        {
            this->base().reserve( n );
        }        
    };

    //
    // clonability

    template< typename T, typename CA, typename A >
    inline ptr_vector<T,CA,A>* new_clone( const ptr_vector<T,CA,A>& r )
    {
        return r.clone().release();
    }

    /
    // swap

    template< typename T, typename CA, typename A >
    inline void swap( ptr_vector<T,CA,A>& l, ptr_vector<T,CA,A>& r )
    {
        l.swap(r);
    }
    
}

ptr_vector的构造函数中,使用this->base().reserve( n ), base()为vector,即先vector.reserve(n),所以下面代码中

  1. boost::ptr_vector< Thread> threads_
  2. threads_(threadNum)
  3. threads_.push_back(new Thread(bind(&Test::threadFunc,this)));
  4. start

没有出现问题。

#include "muduo/base/CountDownLatch.h"
#include "muduo/base/Mutex.h"
#include "muduo/base/Thread.h"
#include "muduo/base/Timestamp.h"
#include <boost/ptr_container/ptr_vector.hpp> 
#include <unistd.h>
#include <algorithm>
#include <vector>
#include <stdio.h>
#include <memory>
#include <iostream>
using namespace muduo;
using namespace std;

class Test
{
	public:
		Test(int threadNum);
		void mainThFun();
		void joinall();
	private:
		void threadFunc();
		//std::vector<std::shared_ptr<Thread>> threads_;
		boost::ptr_vector<Thread> threads_;
		//std::vector<Thread*> threads_;
		CountDownLatch latch_;
};

Test::Test(int threadNum):latch_(threadNum),threads_(threadNum)
{

	for(int i = 0; i< threadNum ; i++)
	{
		//threads_.emplace_back(new Thread(bind(&Test::threadFunc,shared_from_this())));
		threads_.push_back(new Thread(bind(&Test::threadFunc,this)));
	}

	//for_each(threads_.begin(),threads_.end(),[](std::unique_ptr<Thread> ut){ut->start();});
	for_each(threads_.begin(),threads_.end(),bind(&Thread::start,placeholders::_1));


}

void Test::threadFunc()
{
	sleep(2);
	latch_.countDown();
	cout<<"threadFunc is :"<<CurrentThread::tid()<<endl;
}

void Test::mainThFun()
{
	latch_.wait();
	cout<<"mainThFun : "<<CurrentThread::tid()<<endl;

}
void Test::joinall()
{
	for_each(threads_.begin(),threads_.end(),bind(&Thread::join,placeholders::_1));
	cout<<"joinall"<<endl;
}
int main()
{
	//shared_ptr<Test> testcount = make_shared<Test>(3); 不能=指针
	//shared_ptr<Test> testcount(new Test(3));
	Test testcount(3);
	cout<<"let's go!"<<CurrentThread::tid()<<endl;

	testcount.mainThFun();
	testcount.joinall();

	return 0;
}


3.2 ptr_vector性能分析

借用https://www.cnblogs.com/my_life/articles/5452342.html的代码,增加了emplace_back以及make_shared,进行分析。


#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>
#include <cstdio>
#include <ctime>
#include <stdint.h>
 #include <memory>
class TSomeData
{
private:
  int data;
public:
  TSomeData(int d)
    : data(d)
  {
    // Empty
  }
};
 
const int TEST_ITERATIONS = 10000000;
 
typedef std::vector<std::shared_ptr<TSomeData> > TVectorOfShared;
typedef boost::ptr_vector<TSomeData> TPtrVector;
typedef std::vector<std::unique_ptr<TSomeData>> TVectorOfUnique;
int main()
{
  clock_t start;
  clock_t end;
 
  start = ::clock();
  TVectorOfShared vectorOfShared;
  for (int i = 0; i < TEST_ITERATIONS; ++i) {
  // Test vector of shared_ptr
    std::shared_ptr<TSomeData> data(new TSomeData(i));
    vectorOfShared.push_back(data);
  }
  end = ::clock();
  printf("Vector of shared:\n  Time executed: %u\n",
         static_cast<uint32_t>((end - start) / (CLOCKS_PER_SEC/1000)));
 
  start = ::clock();
  TVectorOfShared vectorOfmakeShared;
  for (int i = 0; i < TEST_ITERATIONS; ++i) {
  // Test vector of shared_ptr
    //boost::shared_ptr<TSomeData> data(new TSomeData(i));
    vectorOfmakeShared.push_back(std::make_shared<TSomeData>(i));
  }
  end = ::clock();
  printf("Vector of make_shared:\n  Time executed: %u\n",
         static_cast<uint32_t>((end - start) / (CLOCKS_PER_SEC/1000)));

  start = ::clock();
  TPtrVector ptrVector;
  for (int i = 0; i < TEST_ITERATIONS; ++i) {
  // Test ptr_vector
    TSomeData* data = new TSomeData(i);
    ptrVector.push_back(data);
  }
  end = ::clock();
  printf("PtrVector:\n  Time executed: %u\n",
         static_cast<uint32_t>((end - start) / (CLOCKS_PER_SEC/1000)));

  start = ::clock();
  TVectorOfShared vectorOfSharedemp;
  for (int i = 0; i < TEST_ITERATIONS; ++i) {
  // Test vector of shared_ptr
    //boost::shared_ptr<TSomeData> data(new TSomeData(i));
    vectorOfSharedemp.emplace_back(new TSomeData(i));
  }
  end = ::clock();
  printf("Vector of shared_emplace_back:\n  Time executed: %u\n",
static_cast<uint32_t>((end - start) / (CLOCKS_PER_SEC/1000)));


start = ::clock();
  TVectorOfUnique vectorOfUniqueemp;
  for (int i = 0; i < TEST_ITERATIONS; ++i) {
  // Test vector of shared_ptr
    //boost::shared_ptr<TSomeData> data(new TSomeData(i));
    vectorOfUniqueemp.emplace_back(new TSomeData(i));
  }
  end = ::clock();
  printf("Vector of unique_emplace_back:\n  Time executed: %u\n",
static_cast<uint32_t>((end - start) / (CLOCKS_PER_SEC/1000)));


start = ::clock();
  TVectorOfUnique vectorOfUnique;
  for (int i = 0; i < TEST_ITERATIONS; ++i) {
  // Test vector of shared_ptr
    std::unique_ptr<TSomeData> data(new TSomeData(i));
    vectorOfUnique.push_back(move(data));
  }
  end = ::clock();
  printf("Vector of unique_push:\n  Time executed: %u\n",
static_cast<uint32_t>((end - start) / (CLOCKS_PER_SEC/1000)));


  return 0;
}

在虚拟机上得出如下结果

lxz@lxz-VirtualBox:~/liuxz/testunp$ ./test 
Vector of shared:
  Time executed: 4083
Vector of make_shared:
  Time executed: 5868
PtrVector:
  Time executed: 1859
Vector of shared_emplace_back:
  Time executed: 3486
Vector of unique_emplace_back:
  Time executed: 5137
Vector of unique_push:
  Time executed: 7154
  1. ptr_vector的大规模创建和使用时,效率高
  2. emplace_back相比push_back效率更高
  3. 具体使用哪个要看实际项目需求,另在使用boost的智能指针时比std的更快,具体没研究,不知道是不是错觉。
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值