zoukankan      html  css  js  c++  java
  • 一次多线程臭虫经验——异步日志c++化时

      做任务单用go实现了异步日志,计划c++化练练手。本以为分分钟搞定的事,结果debug到凌晨两点/(ㄒoㄒ)/~~

      第一版关键代码如下:

    AsyncLog::AsyncLog(size_t bufSize, WriteLogFunc func)
        : _curBuf(new Buffer(bufSize))
        , _nextBuf(new Buffer(bufSize))
        , _writeLogFunc(func)
        , _thread([&]{ this->_WriteLoop(bufSize);})
        , _running(true)
    { ::InitializeConditionVariable(&_cond);
      _thread.detach();
    } AsyncLog::~AsyncLog()
    {
    if (_running) Stop(); } void AsyncLog::Append(const void* data, size_t len) { cLock lock(_mutex); _curBuf->append(data, len); if (_curBuf->writableBytes() == 0) { _bufVec.push_back(_curBuf); //FIXME:new可能返回null,不过那会系统已经要跪了吧~ _nextBuf ? (_curBuf = std::move(_nextBuf)) : (_curBuf.reset(new Buffer)); ::WakeConditionVariable(&_cond); } } void AsyncLog::_WriteLoop(size_t bufSize) { BufferPtr spareBuf1(new Buffer(bufSize)); BufferPtr spareBuf2(new Buffer(bufSize)); BufferVec bufToWriteVec; bufToWriteVec.reserve(8); while (_running){ { cLock lock(_mutex); ::SleepConditionVariableCS(&_cond, &_mutex, INFINITE); bufToWriteVec.swap(_bufVec); bufToWriteVec.push_back(_curBuf); _curBuf = std::move(spareBuf1); if (_nextBuf == NULL) _nextBuf = std::move(spareBuf2); } _writeLogFunc(bufToWriteVec);
    if (spareBuf1 == NULL){ spareBuf1 = bufToWriteVec[0]; spareBuf1->clear(); } if (spareBuf2 == NULL){ spareBuf2 = bufToWriteVec[1]; spareBuf2->clear(); } bufToWriteVec.clear(); } } void AsyncLog::Stop() { _running = false; ::WakeConditionVariable(&_cond); _thread.join(); //Notice:阻塞,等待子线程执行结束 }

      共有五处Bug哟~

    • 用std::thread做成员,初始化列表指定子线程启动函数,该函数访问资源时,可能都没初始化好(ctor中调度出去了)
    • Stop逻辑是后添加的,_running成员声明在了末尾,其初始化即位于_thread之后,可能_WriteLoop执行时_running仍为false
    • 构造函数中,习惯性调_thread.detach(),析构函数中又调了_thread.join()
    • 竞态:_WriteLoop中的耗时IO操作_writeLogFunc(bufToWriteVec),可能调度至前台线程,若其继续写入数据,且触发析构或Stop,那多写的数据不会记Log,因为下次loop时_running已是false
    • Lambda表达式,用的引用捕获,this指针尚可,bufSize可是局部栈变量,指不定啥时候被戳死 ( ☉_☉)≡☞o────

      

      光构造函数就占了三 (╯‵□′)╯,所以说c++的抽象数据结构,碰上多线程,作死哇。防范ctor、dtor中途调度到别的线程,是门专业技能——详细可参考陈硕的书《Linux多线程服务端编程》前两章(这书前四章干货爆表)

      后记:写个代码,把所有该犯的错犯了个遍也是够可以的( ▔___▔)y

  • 相关阅读:
    HDU 1698 Just a Hook(线段树成段更新)
    HDU 1247 Hat's Words (map+string)
    python三大框架之一flask中cookie和session的相关操作
    python三大框架之一flask应用
    python三大框架之一(flask介绍)
    pandas中遍历dataframe的每一个元素
    Python中pandas dataframe删除一行或一列:drop函数
    gevent多协程运用
    利用selenium并使用gevent爬取动态网页数据
    使用selenium 模拟人操作请求网页
  • 原文地址:https://www.cnblogs.com/3workman/p/5810827.html
Copyright © 2011-2022 走看看