写了一个多线程日志类,老湿们来帮我看下这个bug是怎么回事

HeFIYing 2014-01-24 03:22:02
问题描述: 在线程函数test()中 把::Sleep(100)去掉主函数就阻塞在threads.join_all()了,
加上就能顺利结束,这是什么原因呢。

//头文件
#pragma once

#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/filesystem.hpp>
#include <queue>
#include <fstream>

namespace MutithreadLog
{
template <typename V>
class SafeQueue
{
public:
typedef typename boost::recursive_mutex::scoped_lock scoped_lock;
SafeQueue(int nCount)
: _limit(nCount)
{
}
void add(V v)
{
scoped_lock lk(_mutex);
while (_limit <= _queue.size())
{
_cond.wait(lk);
}

_queue.push(v);
_cond.notify_one();
}

V pop()
{
scoped_lock lk(_mutex);
while (_queue.size() == 0)
{
_cond.wait(lk);
}

V vRet = _queue.front();
_queue.pop();
_cond.notify_one();
return vRet;
}
int size()
{
scoped_lock lk(_mutex);
return static_cast<int>(_queue.size());
}
bool is_full()
{
scoped_lock lk(_mutex);
if (_limit <= _queue.size())
{
return true;
}
return false;
}
void clearall()
{
scoped_lock lk(_mutex);
while (_queue.size() > 0)
{
_queue.pop();
}
_cond.notify_one();
}
private:
boost::recursive_mutex _mutex;
boost::condition _cond;
std::queue<V> _queue;
unsigned int _limit;
};

enum eLogLevel
{
eNormal = 0,
eWarning,
eError,
eFatal
};

class CLogInfo
{
public:
CLogInfo(const std::string& strLog, const eLogLevel level)
: m_strLog(strLog), m_level(level) {}
~CLogInfo() {}
const std::string& GetLog() const { return m_strLog; }
const eLogLevel GetLogLevel() const { return m_level; }
private:
std::string m_strLog;
eLogLevel m_level;
};
typedef boost::shared_ptr<CLogInfo> CLogInfoPtr;

class CLogger
{
public:
static bool Initialize(const std::string& strPath = "D:\\MyLog\\");
static void UnInitialize();
static bool WriteLog(const std::string& strLog, const eLogLevel level = eNormal);
static void Run();

private:
static SafeQueue<CLogInfoPtr> m_logQueue;
static boost::thread_group m_threadGroup;
static std::ofstream m_ofstream;
};

};


// 源文件

#include "Logger.h"
#include <Windows.h>
#include <sstream>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace MutithreadLog;
using namespace std;

SafeQueue<CLogInfoPtr> CLogger::m_logQueue = SafeQueue<CLogInfoPtr>(1000);
boost::thread_group CLogger::m_threadGroup;
std::ofstream CLogger::m_ofstream;

bool CLogger::Initialize(const std::string& strPath)
{
if (!boost::filesystem::exists(strPath))
boost::filesystem::create_directory(strPath);
string strFileName = strPath + "ImportMonitorLog.txt";
m_ofstream.open(strFileName.c_str(), ios::app);
if (m_ofstream.is_open())
{
m_threadGroup.create_thread(boost::bind(&CLogger::Run));
return true;
}
return false;
}

void CLogger::UnInitialize()
{
m_logQueue.add(CLogInfoPtr());
}

bool CLogger::WriteLog(const std::string& strLog, const eLogLevel level)
{
if (m_ofstream.is_open())
{
m_logQueue.add(CLogInfoPtr(new CLogInfo(strLog, level)));
return true;
}
return false;
}

void CLogger::Run()
{
while(true)
{
CLogInfoPtr logPtr = m_logQueue.pop();
if (logPtr)
m_ofstream << "level:" << logPtr->GetLogLevel() << " " << logPtr->GetLog() << endl;
else
{
m_ofstream.close();
break;
}
}
}

// test code...
void test()
{
int nThreadID = ::GetCurrentThreadId();
while (true)
{
stringstream sstream;
string strTime = boost::posix_time::to_iso_string(boost::posix_time::second_clock::local_time());
int pos = strTime.find('T');
strTime.replace(pos,1,std::string("-"));
strTime.replace(pos + 3,0,std::string(":"));
strTime.replace(pos + 6,0,std::string(":"));
sstream << strTime << " " << nThreadID;
if (!CLogger::WriteLog(sstream.str() , eNormal))
break;
//::Sleep(100);
}
}

int main(void)
{
CLogger::Initialize();
boost::thread_group threads;
for (int i = 0; i < 30; i++)
threads.create_thread(boost::bind(&test));
::Sleep(1000);
CLogger::UnInitialize();
threads.join_all();
getchar();
}


...全文
293 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
HeFIYing 2014-01-28
  • 打赏
  • 举报
回复
感谢大家的回答,大概找到原因了,应该是队列瞬间被填满,消费线程却只有一个,导致众多生产线程因为等待条件变量阻塞,而且消费线程要和众多的生产线程争抢同一个条件变量,导致情况变得更糟糕。 另外那个clearall确实存在严重的问题。已经修改解决了
masterz 2014-01-26
  • 打赏
  • 举报
回复
还可以直接用boost.log
mujiok2003 2014-01-25
  • 打赏
  • 举报
回复
c++ 11 monitor pattern 实现transaction
#include <mutex>
#include <vector>
#include <future>
#include <iostream>


template <typename T>
class monitor
{
public:
  monitor(T t_ = T()) : t(t_){}

  template <typename F>
  auto  operator()(F f) const->decltype(f(t))
  {
    std::lock_guard<std::mutex> lock(m);
    return f(t);
  }
private:
  mutable T t;
  mutable std::mutex m;
};


int main()
{
  std::vector<std::future<void> > fut;
  monitor<std::ostream&> mos{ std::cout };
  for (int i = 0; i < 5; ++i)
  {
    fut.push_back(std::async([&, i]{
      mos([=](std::ostream& cout){
        cout << i << " " << i << '\n';
      });

      mos([=](std::ostream& cout){
        cout << "Hi from " << i << std::endl;
      });
    }));
  }

  for (auto& f : fut) f.wait();
  mos([](std::ostream& st){
    st << "Done\n";
  }
  );

  return 0;
}
mujiok2003 2014-01-25
  • 打赏
  • 举报
回复
当日志线程准备关闭m_ofstream前, 30个工作线程中有多个已经进入wait状态. 之后日志线程完成执行, 谁来唤醒那些在wait中的线程呢?
mujiok2003 2014-01-25
  • 打赏
  • 举报
回复
m_ofstream没有同步访问
icosagon 2014-01-25
  • 打赏
  • 举报
回复
指的是example下的condition.cpp文件
icosagon 2014-01-25
  • 打赏
  • 举报
回复
推荐看一下boost里libs下thread的condition.cpp是怎么实现的。
icosagon 2014-01-25
  • 打赏
  • 举报
回复
SafeQueue实现的有问题吧, boost::condition 只能用在boost::unique_lock<boost::mutex>上, boost::recursive_mutex要用boost::condition_variable_any, 另外,这代码企图用一个条件变量代替2个不同的等待条件,应该有问题,比如,当pop里,非空条件变量在等待时,这时调用clearall,语义上却是非满条件变量的通知,会导致pop执行下去,结果崩溃。 另外就算没有clearall,一个条件变量,add和pop里却是不同的语义,深感怀疑会引起问题。
赵4老师 2014-01-24
  • 打赏
  • 举报
回复
别费劲写了,直接用我这个:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
    #include <windows.h>
    #include <io.h>
#else
    #include <unistd.h>
    #include <sys/time.h>
    #include <pthread.h>
    #define  CRITICAL_SECTION   pthread_mutex_t
    #define  _vsnprintf         vsnprintf
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
    EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
    LeaveCriticalSection(l);
}
#else
void Lock(CRITICAL_SECTION *l) {
    pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
    pthread_mutex_unlock(l);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
    struct tm *now;
    struct timeb tb;

    if (NULL==pszFmt||0==pszFmt[0]) return;
    _vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,logstr);
    flog=fopen(logfilename1,"a");
    if (NULL!=flog) {
        fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
        if (ftell(flog)>MAXLOGSIZE) {
            fclose(flog);
            if (rename(logfilename1,logfilename2)) {
                remove(logfilename2);
                rename(logfilename1,logfilename2);
            }
        } else {
            fclose(flog);
        }
    }
}
void Log(const char *pszFmt,...) {
    va_list argp;

    Lock(&cs_log);
    va_start(argp,pszFmt);
    LogV(pszFmt,argp);
    va_end(argp);
    Unlock(&cs_log);
}
//Log}
int main(int argc,char * argv[]) {
    int i;
#ifdef WIN32
    InitializeCriticalSection(&cs_log);
#else
    pthread_mutex_init(&cs_log,NULL);
#endif
    for (i=0;i<10000;i++) {
        Log("This is a Log %04d from FILE:%s LINE:%d\n",i, __FILE__, __LINE__);
    }
#ifdef WIN32
    DeleteCriticalSection(&cs_log);
#else
    pthread_mutex_destroy(&cs_log);
#endif
    return 0;
}
//1-78行添加到你带main的.c或.cpp的那个文件的最前面
//81-85行添加到你的main函数开头
//89-93行添加到你的main函数结束前
//在要写LOG的地方仿照第87行的写法写LOG到文件MyLog1.log中

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧