Delphi多线程编程之同步读写全局数据

Delphi XE10.2.3多线程大量读和少量写公共资源时,用原子自旋读写锁代替互斥锁提高效率      多线程这玩意就是这么矛盾,使用多线程的初衷是为了提高CPU等资源使用率和加快程序并发处理能力,这是大家都希望看到的,但另一方面却必须为多线程读写公共资源而绞尽脑汁,各种奇葩处理方式层出不穷,利弊自知。     对于一个高性能服务器在处理多数读取,少量写入的场景时,如果还是使用常规的互斥锁方式,显然就不明智了,这种读多写少的场景最适合使用读写锁方式,读取时不加锁,多线程并发读取,效率是最高... 阅读详情

    开始研究最重要的多线程读写全局数据了,结合书上的例子,我修改成下面的情况:


 

 

unit Tst_Thread3U;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    procedure ThreadsDone(Sender: TObject);
  end;

  TMyThread=class(TThread)
  protected
      procedure Execute;override;
  end;

var
  Form1: TForm1;

implementation
{$R *.dfm}

const
  MaxSize=128;
var
  NextNumber:Integer=0;
  DoneFlags:Integer=0;
  GlobalArry:array[1..MaxSize] of Integer;
  Lock:byte;   //1-不同步  2-临界区 3-互斥
  CS:TRTLCriticalSection; //临界区
  hMutex:THandle;  //互斥

function GetNextNumber:Integer;
begin
  Result:=NextNumber;
  inc(NextNumber);
end;

procedure TMyThread.Execute;
var
  i:Integer;
begin
  FreeOnTerminate:=True; //终止后自动free
  OnTerminate:=Form1.ThreadsDone;
  if Lock<>3 then     //非互斥情况
  begin
    if Lock=2 then EnterCriticalSection(CS); //建立临界区
    for i := 1 to MaxSize do
    begin
      GlobalArry[i]:=GetNextNumber;
      Sleep(5);
    end;
    if Lock=2 then LeaveCriticalSection(CS);//离开临界区
  end else      //-------互斥
  begin
    if WaitForSingleObject(hMutex,INFINITE)=WAIT_OBJECT_0 then
    begin
      for i := 1 to MaxSize do
      begin
        GlobalArry[i]:=GetNextNumber;
        Sleep(5);
      end;
    end;
    ReleaseMutex(hMutex);   //释放
  end;
end;

procedure TForm1.ThreadsDone(Sender: TObject);
var
  i:Integer;
begin
  Inc(DoneFlags);
  if DoneFlags=2 then
  begin
    for i := 1 to MaxSize do
      Memo1.Lines.Add(inttostr(GlobalArry[i]));
      if Lock=2 then DeleteCriticalSection(CS); //删除临界区
       If Lock=3 then CloseHandle(hMutex); //关闭互斥
  end;
end;

//非同步
procedure TForm1.Button1Click(Sender: TObject);
begin
  Lock:=1;
  TMyThread.Create(False);
  TMyThread.Create(False);
end;

//临界区
procedure TForm1.Button2Click(Sender: TObject);
begin
  Lock:=2;  
  InitializeCriticalSection(CS); //初始化临界区
  TMyThread.Create(False);
  TMyThread.Create(False);
end;

//互斥
procedure TForm1.Button3Click(Sender: TObject);
begin
  Lock:=3; // 互斥
  hMutex:=CreateMutex(0,False,nil);
  TMyThread.Create(False);
  TMyThread.Create(False);
end;

end.

 

没有临界区和互斥的帮助,两个线程都不断地在Memo1输出,而且数字是乱的。

 

 

 

 

 


 

 

一、临界区

所谓临界区,就是一次只能由一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。

使用临界区的步骤:

1、先声明一个全局变量类型为TRTLCriticalSection;

2、在线程Create()前调用InitializeCriticalSection()过程来初始化,该函数定义是:

void WINAPI InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

类型lpCriticalSection即是Delphi封装的TRTLCriticalSection。

3、在线程的需要放入临界区的代码前面使用EnterCriticalSection(lpCriticalSection)过程来开始建立临界区。在代码完成后用LeaveCriticalSection(lpCriticalSection)来标志临界区的结束。

4、在线程执行完后用DeleteCriticalSection(lpCriticalSection)来清除临界区。这个清除过程必须放在线程执行完后的地方,比如FormDesroy事件中。上面的例子中,若把该过程放在TMyThread.Create(False);后,会产生错误。

 

二、互斥:

互斥非常类似于临界区,除了两个关键的区别:首先,互斥可用于跨进程的线程同步。其次,互斥能被赋予一个字符串名字,并且通过引用此名字创建现有互斥对象的附加句柄。

提示临界区与事件对象(比如互斥对象)的最大的区别是在性能上。临界区在没有线程冲突时,要用10~15个时间片,而事件对象由于涉及到系统内核要用400~600个时间片。

使用互斥的步骤:

1、声明一个类型为Thandle或Hwnd的全局变量,其实都是Cardinal类型。Hwnd是handle of window,主要用于窗口句柄;而Thandle则没有限制。

2、线程Create()前用CreateMutex()来创建一个互斥量。该函数定义为:

HANDLE WINAPI CreateMutex(

  LPSECURITY_ATTRIBUTES lpMutexAttributes,

  BOOL bInitialOwner,

  LPCTSTR lpName:Pchar);

LPSECURITY_ATTRIBUTES参数为一个指向TSecurityAttributtes记录的指针。此参数设为nil,表示访问控制列表默认的安全属性。

bInitalOwner参数表示创建互斥对象的线程是否要成为此互斥对象的拥有者。当此参数为False时,表示互斥对象没有拥有者。

lpName参数指定互斥对象的名称。设为nil表示无命名,如果参数不是设为nil,函数会搜索是否有同名的互斥对象存在。如果有,函数就会返回同名互斥对象的句柄。否则,就新创建一个互斥对象并返回其句柄。

    返回值是一handle。当错误发生时,返回null,此时用GetLastError函数可查看错误的信息。

    利用CreateMutex()可以防止程序多个实例运行,如下例:

Program ABC;

Uses Forms,Windows,…;

{$R *.res}

Var

  hMutex:Hwnd;

Begin

  Application.Initialize;

  hMutex:=CreateMutex(nil,False,Pchar(Application.Title));

  if GetLastError<>ERROR_ALREADY_EXISTS then

  begin

     //项目要运行的咚咚

  end;

  ReleaseMutex(hMutex);

  Application.Run;

End;

在本节的例程中,我们只是要防止线程进入同步代码区域中,所以lpName参数设置为nil。

 

3、在同步代码前用WaitForSingleObject()函数。该函数使得线程取得互斥对象(同步代码)的拥有权。该函数定义为:

DWORD WINAPI WaitForSingleObject(

  HANDLE hHandle,

  DWORD dwMilliseconds);

这个函数可以使当前线程在dwMilliseconds指定的时间内睡眠,直到hHandle参数指定的对象进入发信号状态为止。一个互斥对象不再被线程拥有时,它就进入发信号状态。当一个进程要终止时,它就进入发信号状态。dwMilliseconds参数可以设为0,这意味着只检查hHandle参数指定的对象是否处于发信号状态,而后立即返回。dwMilliseconds参数设为INFINITE,表示如果信号不出现将一直等下去。

 

这个函数的返回值含义:

 

WAIT_ABANDONED  指定的对象是互斥对象,并且拥有这个互斥对象的线程在没有释放此对象之前就已终止。此时就称互斥对象被抛弃。这种情况下,这个互斥对象归当前线程所有,并把它设为非发信号状态

WAIT_OBJECT_0  指定的对象处于发信号状态

WAIT_TIMEOUT 等待的时间已过,对象仍然是非发信号状态

 

再次声明,当一个互斥对象不再被一个线程所拥有,它就处于发信号状态。此时首先调用WaitForSingleObject()函数的线程就成为该互斥对象的拥有者,此互斥对象设为不发信号状态。当线程调用ReleaseMutex()函数并传递一个互斥对象的句柄作为参数时,这种拥有关系就被解除,互斥对象重新进入发信号状态。

注意除WaitForSingleObject()函数外,你还可以使用WaitForMultipleObject()和MsgWaitForMultipleObject()函数,它们可以等待几个对象变为发信号状态。这两个函数的详细情况请看Win32 API联机文档。

 

4、在同步代码结束后,使用ReleaseMutex(THandle)函数来标志。该函数只是释放互斥对象和线程的拥有者关系,并不释放互斥对象的句柄。

5、调用CloseHandle(THandle)来关闭互斥对象。请注意例程中该函数的使用位置。

 

 

Delphi 自带线程池 unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Threading, Vcl.StdCtrls;... 阅读详情

相关推荐

【delphi11】delphi进阶【七、多线程编程在Delphi中的实现】

在深入Delphi的多线程编程之前,首先要理解并发(Concurrency)和多线程(Multithreading)的概念。:并发是指系统能够处理多个任务的能力,这些任务可能会交错执行,但在任意时刻,可能只有一个任务在执行。:多线程是并发的一种形式,它允许在同一个程序中同时运行多个线程来执行不同的任务。

Bobowen的博客 3285

Delphi线程同步(SendMessage)

Delphi线程同种的方法有很多种,除了常用的Synchronize方法,也可以使用SendMessage向主窗口发送消息,因为SendMessage是阻塞的,可以达到同步的效果。主线程可以直接定义消息类型的procedure接收消息,也可以重写TControl类的WndProc窗口过程,在窗口过程里面截取自己想要的消息。 Thread {********************...

386

Delphi语言的多线程编程

在计算机科学中,线程是程序执行的基本单元。一个程序可以由多个线程并行执行,每个线程都可以独立执行任务。资源共享:多个线程可以共享进程的资源(如堆、全局变量等),避免资源冗余。更好的响应性:在用户界面应用中,主线程通常负责处理用户输入,辅助线程可以执行耗时的操作,避免界面冻结。并行处理:在多核处理器上,多个线程可以并行执行,提高程序的整体性能。然而,多线程编程也带来了复杂性,特别是在资源共享和同步方面,容易引发竞争条件等问题。

2501_90406366的博客 1244

Delphi多线程编程之五不同类线程读写全局变量-阻塞和锁定

(调试环境:Delphi2007+WinXPsp3 例程:Tst_Thread5.dpr) 前面的例子都是同类线程的不同实例来读写全局变量,用临界区、互斥等来锁住同段代码。现在碰到的问题是,A,B两个不同类型的线程,如何安全地来读写全局变量。 unit Tst_Thread5U; interface uses Windows, Messages, SysUtils, Var...

dibodang1423的博客 302

Delphi多线程技术:提升数据读写性能

多线程技术是一种允许同时执行多个线程来提高应用程序性能的技术。在Delphi中,通过合理地利用多线程,可以提高软件的响应速度和执行效率。它允许程序在等待某些耗时操作完成的同时,继续执行其他任务。这对于处理像文件I/O、网络通信或复杂计算等需要大量时间的操作尤其有用。在Delphi中,TThread类是实现多线程程序的基本构件。它允许开发者创建和管理后台线程,以执行需要并行处理的任务。TThread类提供了一个框架,简化了线程的创建、启动、执行和终止过程。

weixin_33582089的博客 1191

掌握Delphi多线程数据库编程的核心技术

本文还有配套的精品资源,点击获取 简介:Delphi作为一款高效的快速应用开发工具,支持丰富的多线程数据库编程功能。本文深入介绍Delphi的多线程编程基础、线程类、数据库访问组件、线程间通信、并发与同步、线程安全的数据访问、错误处理与调试以及性能优化等关键知识点,旨在帮助开发者构建出性能优异的数据库应用程序。 1. 多线程编程基础 1.1 线程的基本概念 ...

weixin_35307279的博客 1102

◆Delphi多线程编程之三 同步读写全局数据 ◆(乌龙哈里2008-10-12)

◆Delphi多线程编程之三同步读写全局数据 ◆(乌龙哈里2008-10-12)(调试环境:Delphi2007+WinXPsp3 例程:Tst_Thread3.dpr)     开始研究最重要的多线程读写全局数据了,结合书上的例子,我修改成下面的情况:  unit Tst_Thread3U;interfaceuses  Windows,

发呆茶馆 2829

掌握DELPHI多线程技术:实战指南

在计算机科学中,多线程(Multithreading)是一种编程范式,允许一个程序中包含两个或多个部分同时执行,这些部分被称为线程。每个线程可以视为独立的执行路径,它们共享程序的全局数据,但具有自己的调用栈和程序计数器。多线程能够提升应用程序的性能,允许在等待I/O操作或处理慢速任务时,CPU继续执行其他线程。

weixin_42612804的博客 1141

Delphi多线程编程之三同步读写全局数据

开始研究最重要的多线程读写全局数据了,结合书上的例子,我修改成下面的情况: unit Tst_Thread3U; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls; type TForm...

dibodang1423的博客 213

delphi 多线程 读取数据

用TThread建立独立的单元,将文本文件的数据分割成前后两半部分,用两个线程来分别读取,然后在StringGrid中显示出数据。

泛微OAe9实战:如何从零搭建一个车辆预约系统(附完整代码)

本文详细介绍了如何利用泛微OA e9的建模引擎,从零开始构建一个企业级车辆预约管理系统。文章深入解析了核心数据模型设计、前后端双重预约冲突校验机制,并重点演示了如何配置自定义资源看板以实现车辆使用状态的可视化监控。该系统设计思路通用,可扩展至会议室、设备等各类共享资源的管理。

weixin_28363123的博客 142

[Delphi]Delphi的多线程编程

.style1 { FONT-WEIGHT: bold; FONT-SIZE: 12px; COLOR: #ff6699}.yjx { BORDER-RIGHT: #f0f0f0 0px solid; BORDER-TOP: #f0f0f0 0px solid; BORDER-LEFT: #f0f0f0 0px solid; BORDER-BOTTOM: #f0f0f0 0px

vincent__hu的专栏 1816

养龙虾之_因特尔UHD/ULtra/Arc核显跑大模型_安装ollama_跑qwen3.5:0.8b/9b_使用copaw1.0.1测试_并配置copaw技能效果炸裂---AI大模型应用探索0019

摘要:本文探讨了利用Intel UHD770核显运行大语言模型的可能性。通过使用Intel发布的ipex-llm项目工具包,成功在核显上运行了Qwen3.5系列模型。测试发现0.8B小模型运行流畅(3-5秒响应),但智能程度有限;而9B大模型虽然效果更好,但响应时间长达2分钟,且GPU/CPU占用率不高。实验证明Intel核显确实可以运行LLM,但实际效果和性能表现仍有提升空间,特别是对大模型的支持尚不理想。

添柴程序猿的专栏 230
上一篇: 还CRM以真实
下一篇: sql server行级锁,排它锁,共享锁的使用
VBEND
博客等级 码龄23年 16粉丝 26原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值