DELPHI高性能大容量SOCKET并发(五):锁和对象分离

DELPHI高性能大容量SOCKET并发(九):稳定性问题解决 IOCP接收缓存导致的内存错乱 在用IOCP控件写了一个ERP服务器后,服务器会发生运行3天后,出现莫名的内存错误,用FastMM检测,是本没有内存错误的地方,而且内存错误出现的地方也不固定。这是一个不可重现的Bug,后续通过打日志把错误范围缩小后发现,每次出现内存错误之前都是由于有链接断开释放,因此就加了日志逐步定位到是TSocketHandle释放引起的,具体原因是:在IOCP中,每个S 阅读详情

锁和对象一起封装的危险

在多线程编写中,绝大多数编码风格喜欢把锁和要访问的对象封装在同一个对象,释放对象的时候也释放锁,这样会造成死锁。我们写一个测试例子,我们创建一个锁,把锁锁住,然后再创建一个线程,一直不停的等待锁返回,然后我们把锁释放,这时线程就死锁,代码如下:

定义接口:

type
  TLockObject = class;
  TLockTestThread = class;
  TForm1 = class(TForm)
    btn1: TButton;
    mmoLockThreadTest: TMemo;
    procedure btn1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FLockTestThread: TLockTestThread;
    FLockObject: TLockObject;
  public
    { Public declarations }
  end;

  TLockTestThread = class(TThread)
  private
    FLockObject: TLockObject;
  public
    procedure Execute; override;
    procedure AddLockLog;
    property LockObject: TLockObject read FLockObject write FLockObject;
  end;

  TLockObject = class(TObject)
  private
    FLock: TCriticalSection;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    procedure Lock;
    procedure UnLock;
  end;

var
  Form1: TForm1;
在Form1创建的时候创建锁,并把锁锁住,创建一个线程等待锁返回:

procedure TForm1.FormCreate(Sender: TObject);
begin
  FLockObject := TLockObject.Create;
  FLockObject.Lock;
  FLockTestThread := TLockTestThread.Create(True);
  FLockTestThread.LockObject := FLockObject;
  FLockTestThread.FreeOnTerminate := True;
  FLockTestThread.Resume;
end;
线程的执行方法一直等待锁返回,并写一条日志。由于是窗体创建的时候,锁已经锁住了,因此线程会一直等待:

procedure TLockTestThread.AddLockLog;
begin
  Form1.mmoLockThreadTest.Lines.Add('Lock')
end;

procedure TLockTestThread.Execute;
begin
  inherited;
  while not Terminated do
  begin
    FLockObject.Lock;
    Synchronize(AddLockLog);
  end;
end;
这时线程会一直等待,如果我们把FLockObject释放,线程也会一直等待,造成死锁。

procedure TForm1.btn1Click(Sender: TObject);
begin
  FLockObject.Lock;
  FLockObject.Free;
  FLockObject := nil;
end;

锁和对象分离

有了上面的基础之后,我们就需要把锁和对象分离,在IOCPDemoSvr例子代码中TSocketHandle我们用一个结构体来管理锁和对象,锁在创建之后只有等TSocketHandle释放之后再释放,主要代码是TSocketHandles类,定义单元:

  {* 客户端对应Socket管理对象 *}
  TSocketHandles = class(TObject)
  private
    {* 正在使用列表管理对象 *}
    FList: TList;
    {* 不再使用列表管理对象 *}
    FIdleList: TList;
    {* 锁 *}
    FLock: TCriticalSection;
    {* 获取某一个 *}
    function GetItems(const AIndex: Integer): PClientSocket;
    {* 获取总个数 *}
    function GetCount: Integer;
    {* 清除 *}
    procedure Clear;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    {* 加锁 *}
    procedure Lock;
    {* 解锁 *}
    procedure UnLock;
    {* 添加一个对象 *}
    function Add(ASocketHandle: TSocketHandle): Integer;
    {* 删除 *}
    procedure Delete(const AIndex: Integer); overload;
    procedure Delete(ASocketHandle: TSocketHandle); overload;
    property Items[const AIndex: Integer]: PClientSocket read GetItems; default;
    property Count: Integer read GetCount;
  end;
实现单元:
{ TSocketHandles }

constructor TSocketHandles.Create;
begin
  FList := TList.Create;
  FIdleList := TList.Create;
  FLock := TCriticalSection.Create;
end;

destructor TSocketHandles.Destroy;
begin
  Clear;
  FList.Free;
  FIdleList.Free;
  FLock.Free;
  inherited;
end;

function TSocketHandles.GetItems(const AIndex: Integer): PClientSocket;
begin
  Result := FList[AIndex];
end;

function TSocketHandles.GetCount: Integer;
begin
  Result := FList.Count;
end;

procedure TSocketHandles.Clear;
var
  i: Integer;
  ClientSocket: PClientSocket;
begin
  for i := 0 to Count - 1 do
  begin
    ClientSocket := Items[i];
    ClientSocket.Lock.Free;
    ClientSocket.SocketHandle.Free;
    Dispose(ClientSocket);
  end;
  FList.Clear;
  for i := 0 to FIdleList.Count - 1 do
  begin
    ClientSocket := FIdleList[i];
    ClientSocket.Lock.Free; //释放锁
    Dispose(ClientSocket);
  end;
  FIdleList.Clear;
end;

procedure TSocketHandles.Lock;
begin
  FLock.Enter;
end;

procedure TSocketHandles.UnLock;
begin
  FLock.Leave;
end;

function TSocketHandles.Add(ASocketHandle: TSocketHandle): Integer;
var
  ClientSocket: PClientSocket;
begin
  if FIdleList.Count > 0 then //先在空余中查找
  begin
    ClientSocket := FIdleList[0];
    FIdleList.Delete(0);
  end
  else //否则创建一个
  begin
    New(ClientSocket);
    ClientSocket.Lock := TCriticalSection.Create;
  end;
  ClientSocket.SocketHandle := ASocketHandle;
  ASocketHandle.FLock := ClientSocket.Lock;
  Result := FList.Add(ClientSocket);
end;

procedure TSocketHandles.Delete(const AIndex: Integer);
var
  ClientSocket: PClientSocket;
begin
  ClientSocket := FList[AIndex];
  ClientSocket.Lock.Enter;
  try
    ClientSocket.SocketHandle.Free;
    ClientSocket.SocketHandle := nil;
  finally
    ClientSocket.Lock.Leave;
  end;
  FList.Delete(AIndex);
  if FIdleList.Count > MAX_IDLELOCK then //如果达到最大空闲个数,则释放
    Dispose(ClientSocket)
  else
    FIdleList.Add(ClientSocket);
end;

procedure TSocketHandles.Delete(ASocketHandle: TSocketHandle);
var
  i, iIndex: Integer;
begin
  iIndex := -1;
  for i := 0 to Count - 1 do
  begin
    if Items[i].SocketHandle = ASocketHandle then
    begin
      iIndex := i;
      Break;
    end;
  end;
  if iIndex <> -1 then
  begin
    Delete(iIndex);
  end;
end;

V1版下载地址:http://download.youkuaiyun.com/detail/sqldebug_fan/4510076,需要资源10分,有稳定性问题,可以作为研究稳定性用;

V2版下载地址:http://download.youkuaiyun.com/detail/sqldebug_fan/5560185,不需要资源分,解决了稳定性问题和提高性能;免责声明:此代码只是为了演示IOCP编程,仅用于学习和研究,切勿用于商业用途。水平有限,错误在所难免,欢迎指正和指导。邮箱地址:fansheng_hx@163.com
C#高性能大容量SOCKET并发(一):IOCP完成端口例子介绍 例子主要包括SocketAsyncEventArgs通讯封装、服务端实现日志查看、SCOKET列表、上传、下载、远程文件流、吞吐量协议,用于测试SocketAsyncEventArgs的性能和压力,最大连接数支持65535个长连接,最高命令交互速度达到250MB/S(使用的是127.0.0.1的方式,相当于千兆网卡1Gb=125MB/S两倍的吞吐量)。服务端用C#编写,并使用log4net作为日志模块。 阅读详情

相关推荐

利用Delphi编写Socket通信程序

一、Delphi与Socket  计算机网络是由一系列网络通信协议组成的,其中的核心协议是传输层的TCP/IP和UDP协议。TCP是面向连接的,通信双方保持一条通路,好比目前的电话线,使用telnet登陆BBS,用的就是TCP协议;UDP是无连接的,通信双方都不保持对方的状态,浏览器访问Internet时使用的HTTP协议就是基于UDP协议的。TCP和UDP协议都非常复杂,尤其是TCP协议,为了保

dudunono的专栏 2111

Delphi里面的TCriticalSection对象的用法(如何使用锁)

Delphi里面的TCriticalSection对象是将包含它的对象给锁住。 比如: TAsioDataBuffer = class   procedure Setstate(const Value: Integer); //数据锁 public   FDataLock, FSendLock: TCriticalSection;   Writedata, Se...

dishuo0350的博客 1021

IOCP(Delphi NET)C#高性能大容量SOCKET并发完成端口例子(有C#客户端)完整实例源码修改版

修改StartAccept接收管理错误信息,否则在实际应用中会出现的错误修改StartAccept接收管理错误信息,否则在实际应用中会出现的错误修改StartAccept接收管理错误信息,否则在实际应用中会出现的错误修改StartAccept接收管理错误信息,否则在实际应用中会出现的错误

多线程(六) 死锁和Lock锁

死锁 同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。 synchronzied(A锁){ synchronized(B锁){ } } 我们进行下死锁情况的代码演示: 定义锁对象类 publicclassMyLoc...

qq_41781316的博客 493

Delphi XE10.2.3多线程大量读和少量写公共资源时,用原子自旋读写锁代替互斥锁提高效率

对于一个高性能服务器在处理多数读取,少量写入的场景时,如果还是使用常规的互斥锁方式,显然就不明智了,这种读多写少的场景最适合使用读写锁方式,读取时不加锁,多线程并发读取,效率是最高的,要写入数据时堵塞所有后面进来的读写线程,写完释放锁,所有读取线程又并发读取,详细内容请见:https://blog.youkuaiyun.com/u013420701/article/details/79912242

delphi高性能日志单元

1、包含高性能日志单元inlog,作者高凉新农, 2、包含一个非线程日志单元log4,这是网络上log4me日志单元有改动 3、这个资源主要是宣传delphi开源组件InIocp: InIOCP 是一套基于IOCP(Delphi 版)的开源框架组件,内部作了消息封装,采用独特的 传输机制,帮助快速实现网络开发。 (一)主要功能: 1、原始数据流服务; 2、认证服务:登录、登出、查询客户端; 3、即时消息服务:消息的发送、推送和广播; 4、文件服务:支持大文件上传、下载,支持断点续传、查询磁盘文件等; 5、数据库服务:支持 SQL 命令查询、更新,支持 DataSetPrivoder 更新; 6、自定义服务:客户端自定义传输信息,服务端自定义操作,执行远程函数; 7、HTTP基本服务:一个服务器同时支持 C/S 服务模式和 HTTP 基本服务。

【Delphi】中共享锁实现

如果对象不提供内置的锁定机制,则可以使用临界区。临界区的工作方式类似于允许单个线程在任何时刻进入的门。要使用临界区,创建一个。每个临界区都与您想要保护的全局内存相关联。每个访问该全局内存的线程在访问之前都应首先使用。:临界区仅在每个线程都使用它们来访问关联的全局内存时才有效。如果线程忽略临界区并在不调用。方法,以确保没有其他线程正在使用它。的情况下访问全局内存,可能会引入同时访问的问题。例如,考虑一个应用程序,它有一个全局临界区变量。(用于阻止其他线程执行该区域)和。方法,以便其他线程可以通过调用。

sensor_WU 博客 691

ETL数据集成案例

一个典型的ETL数据集成案例是将多个来源的数据合并成一个数据集。假设有一个公司,该公司使用一些不同的系统来存储不同部门的数据,例如销售数据存储在一个CRM系统中,财务数据存储在一个ERP系统中,人力资源数据存储在一个HR系统中。为了更好地进行报告和分析,该公司决定将所有这些数据集成到一个数据仓库中。以下是实现这一目标的ETL过程的步骤:提取(Extract):首先,需要从每个系统中提取所需的数据。每个系统可能有不同的数据格式和结构,因此需要编写不同的提取程序或查询来提取数据。

runqu的博客 948

socket编程——多线程编程与锁

一、线程 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user th...

ayaya的博客 744

2026年AI技能进阶指南:从提示词工程到智能体开发实战

提示词工程作为AI时代的基础沟通技能,其核心原理是通过系统化的交互方法论实现与大模型的高效对话。这项技术通过明确性、具体性和可操作性的设计原则,显著提升了AI生成内容的质量和实用性。在工程实践层面,提示词工程已经发展出角色扮演、步骤分解等成熟模式,广泛应用于代码生成、技术方案设计等场景。随着AI智能体开发的兴起,提示词工程进一步演化为定义智能体身份认知和行为规范的系统接口设计,成为构建自主AI系统的关键技术基础。掌握从基础提示词到智能体开发的完整技能栈,能够帮助开发者在AI技术浪潮中保持核心竞争力。

weixin_33139275的博客 333

安川机器人SOCKET 通讯

命令 功能 参数 示例 返回 SelectJob 选择程序 JobName LineNo SelectJob TEST 0 #SelectJob,Ok StartJob 启动程序 JobName StartJob TEST #StartJob,Ok Pause 暂停当前程序 - Pause #Pause,Ok Continue 继续当前程序 - Continue #Continue,Ok ServoOn 伺服上电 - ServoOn #ServoOn,Ok ServoOf...

记录学习内容 8285

DELPHI高性能大容量SOCKET并发(二):IOCP完成端口控件封装

IOCP完成端口介绍: 完成端口模型是Windows平台下SOCKET端口模型最为复杂的一种I/O模型。如果一个应用程序需要同时管理为数众多的套接字,而且希望随着系统内安装的CPU数量的增多,应用程序的性能也可以线性提升,采用完成端口模型,往往可以达到最佳的系统性能。 完成端口可以管理成千上万的连接,长连接传文件可以支持5000个以上,长连接命令交互可以支持20000个以上。这么大并发的连

SQLDebug_Fan的专栏 1万+

AMBA-CHI协议详解(七)

本节描述该协议包含的支持系统保序需求的机制。

Crazzy_M的博客 1158

DELPHI高性能大容量SOCKET并发(三):接收、发送、缓存

接收 完成端口是结合重叠端口一起使用的,在接收数据之前,我们需要投递一个接收请求,使用function WSARecv(s: TSocket; lpBuffers: LPWSABUF; dwBufferCount: DWORD;  var lpNumberOfBytesRecvd, lpFlags: DWORD; lpOverlapped: LPWSAOVERLAPPED;  lpCompl

SQLDebug_Fan的专栏 1万+

在VINS-FUSION上处理KITTI数据集(双目+GPS)

VINS-Fusion 是一个开源的实时多传感器状态估计库,主要由香港科技大学的沈邵劼教授领导的研究团队开发。它是 VINS-Mono(单目视觉惯性系统)的扩展,支持多种传感器组合,如双目、立体相机和IMU。

如果想成为中心,那么就到中心去吧。 1205

RT-DETR改进前必看 - Ultralytics调参 1:1还原RT-DETR官方版本实验环境,更方便改进

的项目文件中集成了YOLOv3v5v6v8v9v10rt-detr11以及其中不同大小的模型结构,更方便我们学习和使用。的参数配置在路径下,其中中设置了模型在训练Train验证Val/Test预测Predict可视化Visualize导出Export超参数Hyperparameters、以及跟踪Tracker的相关参数。在调参过程中主要涉及训练Train和超参数Hyperparameters。下面主要介绍一下初始超参数Hyperparameters。

Limiiiing的博客 7959

DELPHI高性能大容量SOCKET并发(十):IOCP完成端口性能优化

IOCP性能优化主要是集中在每个处理接收数据和发送数据的对象锁,如果能降低锁的调用次数和提高锁的效率,对IOCP的整理效率和吞吐量都非常有帮助。有很多开发人员在优化IOCP的时候,对于如何提高锁的效率,有使用原子操作来加锁,这样做的效率比较调用Windows的锁效率,因而吞吐率也更高。我们不讨论提高锁的效率,我们这次的优化是降低锁的调用次数。 服务端优化 IOCP是异步线程通知方式,有

SQLDebug_Fan的专栏 9931

知识图谱-KGE-语义匹配-双线性模型-2018:SimplE

【paper】 SimplE Embedding for Link Prediction in Knowledge Graphs 【简介】 本文是加拿大英属哥伦比亚大学的两位学者发表在 NIPS 2018 上的工作,文章提出了 SimplE(Simple Embedding)。这篇和前面一篇差不多,也是对 1927 年的 CP 进行改进(简化),传统CP为实体分配的两个向量是独立训练的,SimplE利用关系的逆在三元组打分函数中加上了一个对称项,使得每个实体的两个向量依赖学习,并且 SimplE 的复杂度随

u013250861的博客 958

DELPHI高性能大容量SOCKET并发(一):IOCP完成端口例子介绍

例子主要包括IOCP控件封装、服务端实现、传输协议和日志、控制、SQL查询、上传、下载等协议实现,并包括一些初步的性能测试结果。 服务端: 界面截图如下: 提供服务和桌面方式运行,桌面方式可直接打开程序,方便日常调试,可以使用命令行注册或卸载服务,在CMD中输入D:\DEMO\IOCPDemo\Bin\IOCPDemoSvr.exe -install来注册服务,在CMD输入D:\DE

SQLDebug_Fan的专栏 3万+

【LLC快速上手系列】LLC轻载时,死区时间怎么调才对?别再傻傻乱搞了!

死区时间,就是指。如果你死区设得好,就能让开通前 ( V_DS to 0 ),完美实现!

weixin_43199439的博客 1856

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

DELPHI高性能大容量SOCKET并发(六):协议字符集

UTF-8 UTF-8是UNICODE的一种变长字符编码又称万国码,由Ken Thompson于1992年创建。现在已经标准化为RFC 3629。UTF-8用1到6个字节编码UNICODE字符。用在网页上可以同一页面显示中文简体繁体及其它语言(如日文,韩文)。 使用UTF-8的好处是现在一些手机平台都是使用UTF-8,另外在一些嵌入式平台,如果不支持中文,只支持英文,可以不转换,UTF-8就可

SQLDebug_Fan的专栏 6017

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

DELPHI高性能大容量SOCKET并发(八):断点续传

断点续传 断点续传主要是用在上传或下载文件,一般做法是开始上传的时候,服务器返回上次已经上传的大小,如果上传完成,则返回-1;下载开始的时候,由客户端上报本地已经下载大小,服务器根据位置信息下发数据,因此上传下载协议都需要带Size大小,例如我们协议格式。 上传开始: 客户端->服务器 { [Request] Command=Upload Dir=Dir

SQLDebug_Fan的专栏 5744

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

DELPHI高性能大容量SOCKET并发(七):通讯协议

协议种类 开发Socket程序有两种协议类型,一种是用文本描述的,类似HTTP协议,定义字符集,好处是兼容性和调试方便,缺点是解析文本会损耗一些性能;一种是用Code加结构体,定义字节顺序,好处是性能高,缺点是兼容性和调试不方便。这个可以根据应用场景灵活选择,如果您的应用相对稳定,需求变化少,性能要求高,则可以使用Code加结构体的方式。如果您的应用需要不停的扩充功能,但是对性能要求不苛刻,

SQLDebug_Fan的专栏 6730

DELPHI高性能大容量SOCKET并发(四):粘包、分包、解包

粘包 使用TCP长连接就会引入粘包的问题,粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。粘包可能由发送方造成,也可能由接收方造成。TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据,造成多个数据包的粘连。如果接收进程不及时接收数据,已收到的数据就放在系统接收缓冲区,用户进程读取数据时就可能同时读到多个数据包。 粘

SQLDebug_Fan的专栏 1万+
上一篇: DELPHI高性能大容量SOCKET并发(四):粘包、分包、解包
下一篇: DELPHI高性能大容量SOCKET并发(六):协议字符集
SQLDebug_Fan
博客等级 码龄17年 710粉丝 26原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值