多线程程序的填坑笔记和多线程编程应该遵循的规则

简介: 这几天晚上群里一朋友有偿叫我把他的程序弄稳定,因为是现场管理项目,需要做到无人职守,所以即使是客户端,也不能经常down机,因为之前对他的程序有过一个晚上的实地查看,基本流程已经有个大概的了解,我就接下来了。

这几天晚上群里一朋友有偿叫我把他的程序弄稳定,因为是现场管理项目,需要做到无人职守,所以即使是客户端,也不能经常down机,因为之前对他的程序有过一个晚上的实地查看,基本流程已经有个大概的了解,我就接下来了。

刚开始的时候, 程序运行不到一个上午,内存暴涨,有时几个小时就挂了,这个那天晚上发现了,找了半天发现一处加载图片的TMemoryStream没有释放。

没想到接下来还有很多的坑需要填。

 

一个个解决吧,看看有多少坑!

第一天:开始他跟我说他的连接数据库老是超出数量,会导致程序不能处理任务,当天晚上就用diocp3中的BaseQueue做了个轻便的ADO连接池,因为baseQueue做了大量的测试,所以这个ado连接池经过简单的测试就算OK了,接下来就大量的苦力工作了。

 

第二天:说连接池换上后,连接问题没有了,可是线程池不能处理任务了。他之前使用的一个叫uThreadPool单元,代码好复杂。我推荐他使用QWokers,因为qwokers我一直在使用,很稳定了,任务的投递也很简单,后来一想,他是D7,没办法用这么好的类库,没办法,只好iocpTask上了。iocpTask diocp3中的一个任务投递的库, 是个类似qworker的库,不过只有任务投递和执行的功能,比起qworker来说太简单了,不过用iocpEngine稳定性应该也是不错的了。换上后,我接着对iocpTask做了写修改,加强了调试信息,能够很方便的看到工作线程的当前状态,和任务执行的状态。

因为他说线程池有问题,我估计肯定是卡在哪个任务了,到时候卡死的时候一看就明白了。

下面是iocpTask输出的状态信息,

post counter:10000001
response counter:10000001
error counter:0

active : True, worker count: 5
----------------------- woker 1 --------------------
thread id: 9664, response count: 1719388
busying:False, waiting:True, reserved:True 
request state info:
runInMainThread: False, done: True, time(ms): 0
----------------------- woker 2 --------------------
thread id: 6680, response count: 2010817
busying:False, waiting:True, reserved:True 
request state info:
runInMainThread: False, done: True, time(ms): 0
----------------------- woker 3 --------------------
thread id: 4956, response count: 1965637
busying:False, waiting:True, reserved:False 
request state info:
runInMainThread: False, done: True, time(ms): 0
----------------------- woker 4 --------------------
thread id: 8304, response count: 2641407
busying:False, waiting:True, reserved:False 
request state info:
runInMainThread: False, done: True, time(ms): 0
----------------------- woker 5 --------------------
thread id: 9528, response count: 1662752
busying:False, waiting:True, reserved:False 
request state info:
runInMainThread: False, done: True, time(ms): 0

因为是gitHub上面的项目所以我用了E文注释和E文信息,中式英文,都懂的,稍微解释下,

 

post counter:10000001          //代表投递的任务数

response counter:10000001  //响应数

error counter:0                         //投递失败数量

 

下面是工作线程的一些信息,有线程ID, 响应处理的任务数量,

下面的状态比较关键

busying 的true的话就是代表正在执行任务,wating是在等待状态, reserved是代表常驻工作线程

如果busying为true,会跟着出现当前正在执行的任务信息,如果任务都在执行这样线程池达到最多的线程数,就不能处理新的任务了。导致了他程序发生的情况。这样可以根据任务的信息找到对应的过程,去缩小范围查询问题。

 

request state info: 如果任务有备注信息,会显示在这里

runInMainThread:是否在主线程执行的任务,done:任务是否完成, time(ms): 是任务耗用的时间。

有了iocpTask这个坑很快找到了。

 

发现原来临界用了很多,导致了任务死锁…,

明天要接着填临界的坑了。

 

第三天:他的程序临界使用泛滥,当前进入不了临界,导致任务挂起,但是并不是当前临界的问题,是因为有其他任务进入临界没有退出。所以要找出前面的临界,把临界类改造了下。

可以看到临界的当前信息,我把iocpLocker也进行了相应的升级

function TIocpLocker.getDebugINfo: String;
begin
  Result := Format('%s: busycount:%d, try:%s, enter:%s', [self.FName, GetEnterCount, FTryEnterInfo, FEnterInfo]);
end;

可以获取临界的名称,进入尝试进入临界的线程个数,尝试进入临界的信息和已经进入临界的信息。

有了这个数据就可以看到死锁的临界已经进入的临界信息就是代表造成死锁的元凶了。

 

第四天:因为线程里面对UI的访问过多,可以用iocpTask在线程中对 UI的访问部分,通过投递任务的方式投递到主线程中完成工作,这也是个苦力。

 

认真的对他的程序做了改进,并讲解了多线程编程需要注意的地方<后面会总结>,虽然后面程序还有写bug。他还是很爽快的把钱给付了,说即使程序还有问题也值了,通过这次填坑交流知道要注意很多地方。这算是对我这次工作的最大肯定吧。

 

总结:

通过对这次填坑和以往DIOCP群里面一些朋友的问题和做法,我列出下多线程程序编写需要遵循的几点,希望对大家有所帮助:

1.子线程千万不要访问主线程的UI,(memo,Label),我发现这样做的程序员很多,在diocp中经常会用到onConnected/OnDisconnected事件中直接操作主窗体的Memo。导致程序无法正常退出,或者出现卡死主界面的情况,原因我想可以归纳到访问冲突上面,用临界也不能解决问题。很多组件都是靠windows消息驱动,他才不会使用零件去处理消息,所以临界也没办法。你只有老老实实的投递到主线程去完成这部分工作,qworker和iocpTask都可以很好的完成这项工作。

2.线程之间访问共享资源需要用临界,千万不要多个线程同时去处理同一个变量,或者列表,否则就等着出现各种问题吧。

3.数据库连接尽量用连接池去完成,这样既可以减少连接,也可以很好的避免多个线程对同一个连接的使用。

当然还有很多细小的问题,需要自己去注意了。

目录
相关文章
|
8天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
5天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
8天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
15天前
|
安全 程序员 API
|
8天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
34 1
|
12天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
13天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
40 4
|
13天前
|
消息中间件 供应链 Java
掌握Java多线程编程的艺术
【10月更文挑战第29天】 在当今软件开发领域,多线程编程已成为提升应用性能和响应速度的关键手段之一。本文旨在深入探讨Java多线程编程的核心技术、常见问题以及最佳实践,通过实际案例分析,帮助读者理解并掌握如何在Java应用中高效地使用多线程。不同于常规的技术总结,本文将结合作者多年的实践经验,以故事化的方式讲述多线程编程的魅力与挑战,旨在为读者提供一种全新的学习视角。
41 3
|
14天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
29 1
|
18天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
22 3