前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >别在C++代码里乱打日志了,这才是正确的打日志姿势!(二)

别在C++代码里乱打日志了,这才是正确的打日志姿势!(二)

作者头像
用户6557940
发布2022-07-24 16:29:28
3120
发布2022-07-24 16:29:28
举报
文章被收录于专栏:Jungle笔记Jungle笔记

在上一篇文章别在C++代码里乱打日志了,这才是正确的打日志姿势!中,Jungle设计实现了C++日志系统,并将其用于之前已有的小程序中,测试结果也是OK的。那是否就说明这个Log系统没问题呢?

Too young too simple

1

多线程环境

多线程环境测试

接下来Jungle设计一个简单的多线程环境,测试一下上述日志系统,测试代码如下:

代码语言:javascript
复制
#define THREAD_NUM 5
// 全局资源变量
int g_num = 0;

unsigned int __stdcall func(void *pPM)
{
  LOG_INFO("enter");
  Sleep(50);
  g_num++;
  LOG_INFO("g_num = %d", g_num);
  LOG_INFO("exit");
  return 0;
}

int main()
{
  LOG *logger = LOG::getInstance();
  HANDLE  handle[THREAD_NUM];

  //线程编号
  int threadNum = 0;
  while (threadNum < THREAD_NUM)
  {
    handle[threadNum] = (HANDLE)_beginthreadex(NULL, 0, func, NULL, 0, NULL);
    //等子线程接收到参数时主线程可能改变了这个i的值
    threadNum++;
  }
  //保证子线程已全部运行结束
  WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
  return 0;
}

上述代码中,Jungle一共开启了5个线程,理论上打印的日志文件里,TID应该出现5个不同的数值。每个线程里打印全局变量(即全局共享资源)的值。下面是输出的日志,一共运行了两次(第5、6行隔开):

问题来啦!

首先,在第一次运行输出的日志里,出现了乱码!(第1行和第4行),而且看起来该输出log的地方没有完全输出(真的吗?)

其次,在第二次运行输出的日志里,一行log里好像打印了两次日志(第8行)!

问题出在哪里呢?

为什么会出现乱码?仔细看第8行log,其实打印的都是同一个时刻、同一个位置,都是在调用writeLog函数(宏LOG_INFO即是调用writeLog函数)时出现的问题,也就是说在这个时刻,两个线程都跑到函数writeLog里写log,导致logBuffer缓冲区里存放了两次信息。只不过第8行运气较好,每次的编码都保存完整。而第1行和第4行就没这么走运了!(logBuffer里已经完全乱了!)所以根本问题是,多个线程在同一个时刻访问了同一个资源!所以针对多线程环境,我们需要做到共享资源的互斥

线程安全的日志系统

在单例模式的设计实现里已经提到了线程安全,Jungle用互斥锁达到了互斥的目的。本文也可以使用互斥锁(并且在日志对象实例的单例模式中已经使用),但在这里Jungle想用另一种方法:临界区。

在Log类成员里声明一个CRITICAL_SECTION对象criticalSection,初始化时:

代码语言:javascript
复制
InitializeCriticalSection(&criticalSection);

当然,最好在释放资源时加上下述代码:

代码语言:javascript
复制
DeleteCriticalSection(&criticalSection);

而在进入writeLog时和离开writeLog时加上下述代码:

代码语言:javascript
复制
int LOG::writeLog(...)
{
  int ret = 0;

  EnterCriticalSection(&criticalSection);

        // do something

  LeaveCriticalSection(&criticalSection);

  return 0;
}

需要提及的是,最好是在LeaveCriticalSection之后再DeleteCriticalSection。

接下来再在多线程环境里测试,Jungle测试了几次,但为了缩短篇幅,只展示一次的结果:

可以看到,日志完整记录了每个线程的运行过程(线程号TID不同)。

2

注意事项

尽管上述已经基本实现了日志系统,但仍有很大的改进空间,在调试代码和查阅资料的过程中,Jungle发现需要注意以下几个问题:

(1)字符编码问题:宽字符、ANSI编码等多种不同编码的兼容;

(2)Visio Studio版本的差异:Jungle本想将日志系统应用到之前设计的一个机器人仿真控制器里,但遗憾的是编译不通过,因为那个代码是用Visio Studio 2008写的,而Mutex是C++2011标准的内容,需要用支持该新标准的编译器,比如VS2012及以上版本。(当然了,可以用临界区等其他方法实现互斥,这里Jungle只是提出这个需要注意的问题);

(3)关于宏_CRT_SECURE_NO_WARNINGS:是的,需要在预处理器里加上这个宏或者代码里显示声明这个宏,否则编译不通过,如下图。原因是代码中使用的wcscat等函数不安全,可能会造成内存泄露等。解决方法除了前述提到的声明宏以外,还可以使用更安全的函数。

上述代码资源地址:https://github.com/FengJungle/Log

最后,推荐两篇不错的关于日志系统的文章:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Jungle笔记 微信公众号,前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档