实时多线程系统的日志实现

实时多线程系统的日志实现

2008-03-21 09:19 黄明/戴颖  软件世界 我要评论(0) 字号:T | T
一键收藏,随时查看,分享好友!

为了分析软件系统在测试和运行期产生的故障,目前大多数软件系统所广泛使用的一种方法就是日志记录。本文给出了利用循环缓冲区和单独的日志读写线程实现实时系统的日志功能。

AD:


    为了分析软件系统在测试和运行期产生的故障,目前大多数软件系统所广泛使用的一种方法就是日志记录。但系统在保存日志时需要大量的磁盘I/O操作,同时由于多线程并发互斥访问文件系统可能造成的阻塞,会引起实时软件系统的性能下降,严重时造成整个系统停止响应。本文给出了利用循环缓冲区和单独的日志读写线程实现实时系统的日志功能。

    “如果有两种方式可以编写出没有错误的程序,那么只有第三种方式是有效的。” —ALGOL语言之父Alan J. Perlis。

    任何软件系统,都不可避免的存在设计上的BUG,发现BUG和解决BUG贯穿于软件系统的整个生命期。当软件系统产生非预期的结果时,分析故障原因成为首要任务。对于非实时的单线程程序,可采取设置断点、单步跟踪等手段,能比较容易地确定故障点。然而对于实时的多线程软件系统来说,这些方法不能满足实时性要求,从而在调试过程中往往使系统变得不可用。因此日志记录成为关键的计算机应用系统的生存期中一件非常重要的活动。通过分析在系统出现故障时的日志,即可确定故障原因。在运行过程中,系统将不断地产生跟踪数据,并将其写入到磁盘上的文本文件中。通常的做法是在程序运行的关键点把系统的状态信息写入磁盘,然而这种做法的副作用就是,由于磁盘I/O操作所耗费的时间往往远大于系统中实际工作线程的执行时间,甚至一次磁盘操作消耗的时间比一个线程的整个生命期还要长,这就影响了线程的执行速度,再考虑到线程间的同步运行,其他线程可能会等待该线程的执行结果,这样就会造成整个系统性能的下降,不能满足实时系统的要求。同时由于可能不只一个线程在记录运行日志,因此会有多个线程并发的访问磁盘,造成线程间的长时间阻塞和等待,更大大影响了系统运行时的响应速度,严重时可能造成系统停止响应。这种情况在实时性要求高的系统中是不允许的。

    笔者在最近为IPSWITCH系统开发的通信网关中,利用日志线程和循环缓冲区解决了上述问题。本文把所有用来实现系统自身功能的线程统称为“工作线程”,用于读写磁盘记录日志的线程称为“日志线程”,一个可循环使用、被所有线程共享的固定大小的内存区域称为“循环缓冲区”。

    在工作线程中,在需要记录日志的地方把程序运行的状态信息写入循环缓冲区,再由单独的日志线程读出并写入磁盘保存。在工作线程把日志写入缓冲区时占用的时间很少,基本不影响工作线程运行。在日志线程进行磁盘I/O操作时,不占用工作线程的运行时间,同时由于只有一个单独的日志线程对单个日志文件进行读写,因此完全避免了由于并发操作同一文件而造成的线程间的阻塞。

    以下是实现上述功能的伪代码(根据面向对象思想设计成日志类):

    日志类说明

    Class ClassLogThread

    private:

    var

    Head//循环缓冲区写入指针

    Tail//循环缓冲区读取指针

    LogCount//缓冲区中待处理日志记数器

    Buffer[1024]//循环缓冲区(大小可根据实际情况在这里是一个字符串数组,数组中每个元素代表一行日志)

    Lock//同步变量,用于多线程同时读写循环缓冲区并发控制

    F//日志文件句柄

    procedureExecute//线程函数体,实现日志线程功能

    public:

    procedureADD(状态信息)//工作线程调用,把状态信息写入缓冲区

    日志类实现

    构造函数

    create

    begin

    getmem(Buffer)//申请缓冲区

    Head <- 0 //初始化指针

    Tail <- 0

    LogCount <- 0//初始化待处理日志记数器

    F <- Open(Filename)//创建或打开日志文件

    Createthread(logthread,execute)//创建日志线程并指定线程函数

    end

    析构函数

    destroy

    begin

    close(F)//关闭日志文件

    free(Buffer)//释放缓冲区

    terminate(logthread)//中止释放日志线程

    free(logthread)

    end

    线程函数体

    Execute

    Begin

    While not terminate do//循环直到线程被中止

    begin

    If LogCount > 0 then //缓冲区中有待处理日志

    Writeln(f,Buffer[tail])//写日志到文件

    Tail <- Tail + 1 mod sizeof(buffer)//循环移动指针

    Lock.Acquire//互斥访问

    LogCount <- LogCount-1

    Lock.Release//释放互斥资源

    Endif

    end

    end

    公有成员函数:ADD

    ADD(状态信息)//工作线程(或住线程)调用,把状态信息写入缓冲区

    Begin

    Lock.Acquire

    If LogCountBuffer[Head] <- 状态信息

    Head <- head + 1 mod sizeof(buffer)

    LogCount <- LogCount + 1

    Endif

    Lock.Release

    End

    在程序的初始化部分创建一个日志类的实例

    Log<-ClassLogThread.create

    在工作线程中把状态信息写入缓冲区

    ……

    i <- func(j)

    Log.ADD(timetostr(now) + inttostring(threadID) + “current i is ” + inttostring(i))

    ……

    在以上的代码可以看到,在工作线程中用读写内存代替磁盘I/O操作,保证了工作线程以及系统的响应速度。同时我们也注意到,工作线程把日志写入缓冲区时同样需要线程间的互斥访问,否则就会产生意想不到的后果。通常,每个工作线程在写入缓冲区之前都要锁定共享变量LogCount和Head,在完成操作时释放该资源。但由于锁定的只是共享内存变量而非磁盘资源,因此用在同步上的时间开销同样很少。为了减少锁定资源给程序带来的负面影响,在设计时应注意最小化锁定对象,尽量把无需锁定的代码放在锁定范围之外,例如线程函数Execute中判断是否有待处理日志时,并没有锁定LogCount,同时由于Tail变量和磁盘操作都是日志线程单独操作,也被放在锁定范围之外,因此不会影响其他工作线程的运行。

    一些需要注意的事项和提示:

    ◆在系统开始运行时就应分配好缓冲区,并且在线程中读写缓冲区时应注意缓冲区的边界,否则可能造成内存溢出。
    ◆在代码中可以看到,当缓冲区满时,可能会丢弃一些日志,因此为了保存所有日志,应该把缓冲区设置得尽可能大,同时精心设计需要日志记录的关键点,减少不必要的日志记录。
    ◆在日志中记录工作线程的ID,以便在日后分析时方便的提取同一线程的日志。
    ◆在日志中加时间戳,这样可方便分析不同线程之间的同步关系。
    ◆对于有大量线程同时进行日志记录的系统来说,这些线程将在获得和释放锁上花费大部分的时间,会影响整体系统的性能。这时可以创建多个日志线程类的实例,各自独立操作不同的缓冲区和文件,减少共享冲突的发生。在工作线程中,把日志分类,根据不同的日志类型,写入到不同的缓冲区,再由相应的日志线程写入不同文件。

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353813.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    51NOD 1424 零树

    Discription 有一棵以1为根的树&#xff0c;他有n个结点&#xff0c;用1到n编号。第i号点有一个值vi。 现在可以对树进行如下操作&#xff1a; 步骤1&#xff1a;在树中选一个连通块&#xff0c;这个连通块必须包含1这个结点。 步骤2&#xff1a;然后对这个连通块中所有结点的值…

    nosql的数据服务_使用NoSQL实现实体服务–第1部分:概述

    nosql的数据服务在过去的几周中&#xff0c;我一直在进行一些研发工作&#xff0c;以了解使用NoSQL数据库实现实体服务 &#xff08;也称为数据服务&#xff09;的优势。 实体服务是Thomas Erl的《服务技术》丛书中提出的服务分类。 它用于描述高度不可知和可重复使用的服务&am…

    Java中获取当前函数名

    Java中获取当前函数名 博客分类&#xff1a; Java JavathreadJDKIDEA 有时候我们需要在程序中获取当前运行的函数名&#xff0c;如何简单的做到这点呢&#xff1f;我们可以用getStackTrace轻松搞定。 一提到getStatckTrace多数人会联想到Thowable中的getStackTrace方法。的确&…

    day19 复习,组合和继承

    组合&#xff1a;当一个类的对象作为另一个类对象的属性就叫组合 表示的一种什么有什么的关系 面向对象的思想 不关心程序的执行过程 关心的事一个程序中的角色以及角色与角色的关系 python中一切皆对象 实例化的过程 创建一个对象 __init__给对象添加属性&#xff0c;对…

    Spring Boot和数据库初始化

    Spring Boot是一个很好的框架&#xff0c;可在开发Spring应用程序时为开发人员节省大量时间和精力。 它的主要功能之一是数据库初始化。 您可以使用spring boot来初始化您的sql数据库。 我们将从gradle文件开始 group com.gkatzioura version 1.0-SNAPSHOTapply plugin: java…

    学习Java: Queue

    15 08, 2007 学习Java: Queue Java — 作者 zybing 15:17Java提供了Quere&#xff0c;相当好用&#xff0c;在1.5版本中又有增强。Queue&#xff1a; 基本上&#xff0c;一个队列就是一个先入先出&#xff08;FIFO&#xff09;的数据结构 offer&#xff0c;add区别&#xff1…

    建站手册-网站主机:电子商务主机

    ylbtech-建站手册-网站主机&#xff1a;电子商务主机1.返回顶部 1、http://www.w3school.com.cn/hosting/host_ecommerce.asp2、2.返回顶部1、如果您的公司正在销售某种产品或服务&#xff0c;那么电子商务也许是做生意的一种好办法。 Internet 商业 电子商务就是在 Internet 上…

    ConcurrentLinkedQueue

    ConcurrentLinkedQueue 因为ConcurrentLinkedQueue是线程序安全的并且是针对并发的 主类 Java代码 public class conn{ public static void main(String[] args) throws Exception{ Queue<String> queuenew ConcurrentLinkedQueue<String>(); …

    【大型网站技术实践】初级篇:借助Nginx搭建反向代理服务器

    每天学习一点点 编程PDF电子书、视频教程免费下载&#xff1a;http://www.shitanlife.com/code一、反向代理&#xff1a;Web服务器的“经纪人” 1.1 反向代理初印象 反向代理&#xff08;Reverse Proxy&#xff09;方式是指以代理服务器来接受internet上的连接请求&#xff0c;…

    剖析java中的String之__拼接

    剖析java中的String之__拼接 分类&#xff1a; java 2011-08-24 17:46 31人阅读 评论(0) 收藏 举报 出处, http://blog.csdn.net/izard999/article/details/6708433 网上剖析String的不少&#xff0c;关于其他的String的知识我就不累赘去说了&#xff01; 本文只解释下我在面…

    mfc 弹簧_整合弹簧,速度和瓷砖

    mfc 弹簧我喜欢 Tiles&#xff0c; 并且听到了很多有关 Velocity的信息 。 它们似乎有不同的用途&#xff0c;并且据说很容易结合在一起&#xff0c;所以我决定试一试&#xff0c;并在Spring Web应用程序中同时使用它们。 集成实际上花费了许多小时&#xff0c;并且是一次真正的…

    STM32-RS485通信软硬件实现

    OS&#xff1a;Windows 64 Development kit&#xff1a;MDK5.14 IDE&#xff1a;UV4 MCU&#xff1a;STM32F103C8T6/VET6 AD&#xff1a;Altium Designer 18.0.12 1、RS485简介  RS-485又名TIA-485-A, ANSI/TIA/EIA-485或TIA/EIA-485。RS485是一个定义平衡数字多点系统中的驱动…

    MFC如何使控件大小随着对话框大小自动调整

    MFC如何使控件大小随着对话框大小自动调整 2012-04-27 16:24:50| 分类&#xff1a; MFC | 标签&#xff1a; |字号大中小 订阅 对话框的大小变化后&#xff0c;假若对话框上的控件大小不变化&#xff0c;看起来会比较难看。下面就介绍怎么让对话框上的控件随着对话框的…

    Spring MVC整合Ehcache缓存框架

    https://blog.csdn.net/u012562943/article/details/52289433转载于:https://www.cnblogs.com/pjlhf/p/8818747.html

    Java 枚举7常见种用法

    DK1.5引入了新的类型——枚举。在 Java 中它虽然算个“小”功能&#xff0c;却给我的开发带来了“大”方便。 用法一&#xff1a;常量 在JDK1.5 之前&#xff0c;我们定义常量都是&#xff1a; publicstaticfianl....。现在好了&#xff0c;有了枚举&#xff0c;可以把相关的常…

    Spring和Hibernate的自定义审核日志

    如果您需要对所有数据库操作进行自动审核 &#xff0c;并且正在使用Hibernate…&#xff0c;则应使用Envers或spring data jpa auditing 。 但是&#xff0c;如果由于某些原因您不能使用Envers&#xff0c;则可以使用休眠事件侦听器和spring事务同步来实现类似的功能。 首先&a…

    深度学习训练数据打标签过程

    深度学习训练数据打标签过程 为了获取大量的图片训练数据&#xff0c;在采集数据的过程中常用视频的方式采集数据&#xff0c;但对于深度学习&#xff0c;训练的过程需要很多的有有标签的数据&#xff0c;这篇文章主要是解决视频文件转换成图片文件&#xff0c;并加标签&#x…

    java 枚举学习--从小程序中学习

    java 枚举学习--从小程序中学习 Java 枚举类型 解析 简介&#xff1a;java中枚举是一个类 用之前我觉得还是要知道应该何时使用&#xff1a; 一条普遍的规律是&#xff0c;任何使用常量的地方&#xff0c;例如目前使用的switch 代码切换的地方。 如果只是单独一个值&am…

    《修改代码的艺术》读书笔记一

    一、修改软件的起因及其本质。 修改软件是任何一个开发人员所面对的问题&#xff0c;软件是否容易修改&#xff0c;被修改后的软件是否变得更好&#xff0c;是每一个开发人员都知道必须关注但是在实际开发过程中却往往忽视的问题。有多少人在接手一个新项目时抱怨新项目的遗留代…

    Java基础笔记 – 枚举类型的使用介绍和静态导入

    Java基础笔记 – 枚举类型的使用介绍和静态导入 本文由 arthinking 发表于404 天前 ⁄ Java基础 ⁄ 暂无评论 ⁄ 被围观 1,433 views 1、枚举&#xff08;Enum&#xff09;&#xff1a;JDK5.0中加入了枚举类型&#xff0c;使用enum关键字定义&#xff0c;可以按照如下定义&am…