Delphi入门

Delphi入门var 变量名 数据类型 不指定常量类型指定常量类型 子界 Subrange 是 Pascal 语言中的一个概念 它允许程序员定义一个变量的取值范围 以确保该变量只接受特定范围内的值

大家好,欢迎来到IT知识分享网。

Delphi入门

一、初识

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.单元文件

在 Delphi 中,单元(Unit)是组织代码的基本结构。每个单元通常包括接口部分、实现部分以及可选的初始化和结束部分。

在这里插入图片描述

一个 Delphi 单元文件(.pas 文件)通常分为四个主要部分:

  • interface:接口部分,声明对外可见的常量、类型、变量和过程/函数。
  • implementation:实现部分,定义接口部分声明的过程/函数的具体实现。
  • initialization:初始化部分,包含单元初始化时要执行的代码(可选)。
  • finalization:结束部分,包含单元结束时要执行的代码(可选)。

1.1 interface部分

用途:

interface 部分声明单元对外公开的内容,包括常量、类型、变量、过程和函数等。这些声明可以被其他引用此单元的单元或程序使用。

组成

  • uses:声明此单元依赖的其他单元。任何放在 uses 子句中的单元都将被引入并可用于当前单元的接口部分。
  • 声明的常量、类型、变量、过程和函数等。

1.2 implementation部分

用途implementation 部分包含了 interface 部分中声明的过程和函数的实现细节。这部分的内容对于使用该单元的其他单元或程序是不可见的。

组成

  • uses:如果需要在实现部分引用其他单元,这里可以再次使用 uses 子句声明。这部分的 uses 子句仅对实现部分有效。
  • 具体的过程和函数的实现代码。

1.3 initialization部分

用途initialization 部分用于定义单元初始化时要执行的代码。当单元被加载时,这部分代码会自动执行。

注意:在 initialization 部分中,你可以执行一些初始化操作,如变量的初始赋值、对象的创建等。

1.4 finalization部分

用途finalization 部分定义了在单元卸载时要执行的代码,通常用于清理资源、释放内存等操作。

注意:这部分的代码在程序退出时自动执行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



二、基本数据类型

  • 整型:

在这里插入图片描述

  • 实数:

在这里插入图片描述

  • 字符类型

在这里插入图片描述

  • 布尔类型

在这里插入图片描述

1.定义一个变量

1.1 变量的声明

var 变量名:数据类型;

1.2 变量的赋值

变量名:=变量的值;

在这里插入图片描述

1.3 变量的定义

在这里插入图片描述

2.定义一个常量

  • 不指定常量类型

在这里插入图片描述
在这里插入图片描述

  • 指定常量类型
    在这里插入图片描述

3.定义一个枚举类型

在这里插入图片描述
在这里插入图片描述

4.定义一个子界

“子界”(Subrange)是 Pascal 语言中的一个概念,它允许程序员定义一个变量的取值范围,以确保该变量只接受特定范围内的值。这在编写安全和高效的代码时非常有用,因为它通过限制值的范围来减少错误的发生。子界类型可以用于整数、字符和枚举类型等。

在这里插入图片描述

type 子界名 = 下界..上界; 

5.定义一个集合

在 Pascal 语言中,集合(Set)是一种非常有用的数据结构,它可以存储一组同一类型的元素,并且提供了方便的操作来检查元素是否属于集合、添加或删除元素、以及进行集合的并集、交集和差集操作。

语法

type SetName = set of ElementType; 

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5.1 集合的赋值

在这里插入图片描述
在这里插入图片描述

5.2 集合的并交差运算

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.3 集合的关系运算

6.定义一个记录类型

在 Pascal 中,记录类型(Record)是一种非常有用的数据结构,用于将不同类型的多个数据元素组合在一起,形成一个逻辑上的整体。这种类型特别适合表示复杂的数据结构,比如表示学生信息、员工信息、点的坐标等。

在这里插入图片描述

  • 实现一个学生信息管理系统

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.定义一个指针类型

指针是一个变量,它存储另一个变量的内存地址。通过指针,你可以访问、修改该地址中的数据。

在这里插入图片描述

var P: ^Integer; // 声明一个指向 Integer 类型的指针 X: Integer; begin X := 42; P := @X; // P 指向变量 X 的地址 Writeln(P^); // 输出 42,即通过指针访问 X 的值 end; 

在这个例子中,P 是一个指向 Integer 类型的指针。@X 取得变量 X 的内存地址,然后将其赋值给 PP^ 表示指针 P 指向的地址所存储的值,即 X 的值。

指针在动态内存分配中非常重要。Delphi 提供了 NewDispose 过程来分配和释放内存。

  • New:为指针分配内存。
  • Dispose:释放指针所指向的内存。
var P: ^Integer; begin New(P); // 分配内存 P^ := 100; // 给指针指向的内存赋值 Writeln(P^); // 输出 100 Dispose(P); // 释放内存 end; 

在这里插入图片描述

  • 无类型指针
    在这里插入图片描述

三、运算符

在这里插入图片描述

  • 算术运算符: + – / * div(整除) mod(求模、求余数)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 关系运算符
    在这里插入图片描述

四、程序流程控制

1.普通语句

1.1 赋值语句

在这里插入图片描述

1.2 复合语句

在这里插入图片描述

1.3 With语句

with 语句是 Delphi 中的一种语法结构,用来简化对对象或记录的多个属性或字段的访问。通过使用 with 语句,你可以避免多次重复地写对象或记录的名称,从而使代码更简洁、易读。

当你使用 with 语句时,Delphi 会在 with 块内部自动处理对象或记录的名称。在 with 块中,你可以直接访问该对象或记录的属性或字段,而不必重复写对象或记录的名称。

在这里插入图片描述

2.控制语句

2.1 选择语句

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述




2.2 循环语句

2.2.1 for循环

for 循环是一种计数循环,用于在已知重复次数的情况下执行代码块。for 循环有两种形式:for...to...dofor...downto...do

在这里插入图片描述

2.2.2 while循环

while 循环是一种前测试条件的循环,它在每次迭代前首先检查条件是否为真。如果条件为真,则执行循环体;如果为假,则退出循环。适用于循环次数不确定,但需满足某个条件时。

在这里插入图片描述

2.2.3 repeat循环

repeat...until 循环是一种后测试条件的循环,它先执行一次循环体,然后检查条件。如果条件为假,循环继续执行;如果条件为真,循环结束。适用于至少需要执行一次循环体的情况。

在这里插入图片描述

3. 异常

在这里插入图片描述

五、数组

数组(array)是 Pascal 中一种非常重要的数据结构,用于存储具有相同类型的一组元素。数组允许通过索引访问元素,并且索引通常是整数类型。数组的大小可以在编译时确定,或者在运行时通过动态分配确定。

在这里插入图片描述

1.静态数组

静态数组在编译时定义大小,并且其大小在程序执行期间不可更改。

语法:

type ArrayName = array[IndexType] of ElementType; 
  • IndexType 是用于索引数组的类型,通常是整数类型或枚举类型。
  • ElementType 是数组中元素的类型。

在这里插入图片描述

  • 遍历:

在这里插入图片描述

2.动态数组

动态数组是在运行时分配大小的数组,可以在程序执行过程中调整其大小。动态数组使用 SetLength 函数来分配和调整大小。

在这里插入图片描述

  • 其他初始化方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.多维数组

多维数组是 Pascal 语言中的一种数据结构,它允许你在单个变量中存储多个维度的相同类型的元素。多维数组特别适用于表示矩阵、表格或其他复杂的数据结构。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、例程

例程通常包括两种主要形式:过程(Procedure)和函数(Function)

过程(Procedure)

  • 定义:过程是一个可以执行特定任务的代码块,但不返回任何值。
  • 用途:过程通常用于执行某些动作,比如显示信息、处理数据、执行计算或调用其他例程。

函数(Function)

  • 定义:函数是一个执行特定任务的代码块,它除了执行动作之外,还会返回一个值。
  • 用途:函数通常用于计算结果或获取特定值。

![外链图片转存失败,源站可能有防盗链机制,建议将图

1. 参数传递

在这里插入图片描述

  • 按值传递(By Value)
    • 概念:按值传递是将参数的值复制一份传递给过程或函数。在过程或函数内部对参数的修改不会影响到外部的变量,因为传递的是参数的副本。
    • 默认行为:在 Delphi 中,所有不加修饰符的参数默认是按值传递的。
  • 按引用传递(By Reference)
    • 概念:按引用传递是将参数的内存地址传递给过程或函数,因此在过程或函数内部对参数的修改会直接影响到外部的变量。
    • 在 Delphi 中,可以使用 var 关键字来指定按引用传递。
  • 常量传递(By Const)
    • 常量传递是将参数按值传递,但参数在过程或函数内部是只读的,不能修改其值。常量传递的效率通常比按值传递高,尤其是在传递结构较大且不需要修改的参数时。
    • 在 Delphi 中,使用 const 关键字指定常量传递。
  • out 参数传递的特点
    • var 参数一样,out 参数也是按引用传递的,这意味着传递的是变量的地址而不是值。因此,在过程或函数内部对 out 参数的任何修改都会影响调用者提供的变量。
    • 在传递给过程或函数之前,out 参数不需要被初始化。这与 var 参数不同,var 参数通常要求在调用前进行初始化。
    • 当一个变量作为 out 参数传递时,Delphi 会自动将其清零或重置为默认值(对于对象类型,设置为 nil)。这意味着你不需要显式地初始化这个变量,也不需要担心之前的值。
    • out 参数设计的主要目的是将值从过程或函数返回给调用者。通常情况下,out 参数用于那些只需要从过程或函数获取值的场景,而不需要使用过程或函数传入的初始值。

2. 变量作用域

在这里插入图片描述

2.1 公有变量(Public Variable)

  • 声明部分interface
  • 作用范围:整个 pas 文件,以及其他引用了该 pas 文件的单元。

公有变量是在 unitinterface 部分声明的变量。由于 interface 部分对其他单元是公开的,这些变量可以在当前单元及任何引用该单元的其他单元中使用。

公有变量通常用于在多个单元之间共享数据,或者在需要跨单元访问某些数据时使用。

unit Unit1; interface var SharedVariable: Integer; // 公有变量,可以在其他单元中访问 implementation end. 

2.2 私有变量(Private Variable)

  • 声明部分implementation
  • 作用范围:仅限于当前 pas 文件。

说明

  • 私有变量是在 unitimplementation 部分声明的变量。由于 implementation 部分仅对当前单元可见,其他单元无法访问这些变量。
  • 私有变量用于在单元内部保存状态或数据,不希望这些数据被其他单元访问。
unit Unit1; interface implementation var InternalVariable: Integer; // 私有变量,仅限于当前单元内访问 end. 

2.3局部变量(Local Variable)

  • 声明部分:过程/函数内部
  • 作用范围:仅限于声明它的过程或函数内部。

说明

  • 局部变量是在过程或函数内部声明的变量。这些变量的作用范围仅限于声明它们的过程或函数内部,一旦过程或函数执行结束,这些变量就会被销毁。
  • 局部变量用于存储在过程或函数执行期间所需的临时数据。
procedure MyProcedure; var LocalVariable: Integer; // 局部变量,仅在该过程内有效 begin LocalVariable := 10; end; 

2.4全局变量(Global Variable)

  • 声明部分:过程/函数外
  • 作用范围:整个程序。

说明

  • 全局变量是在单元的 interfaceimplementation 部分声明的变量,但它们在整个程序中都可以访问。通常,Delphi 的全局变量指的是在 interface 部分声明的变量,因为它们在多个单元中可见。
  • 全局变量用于在整个程序的多个部分之间共享数据。需要注意的是,全局变量的使用应当谨慎,以避免数据的意外修改或程序的可维护性下降。
var GlobalVariable: Integer; // 全局变量,整个程序中都可以访问 procedure MyProcedure; begin GlobalVariable := 100; end; 

七、类与对象

类是对象的蓝图,它定义了对象的属性(字段、变量)和行为(方法、函数、过程)。在 Delphi 中,类使用 class 关键字定义。

在这里插入图片描述

1.类的成员

字段(Field)

字段是类中的变量,用来存储对象的状态。字段可以是私有的、受保护的或公有的。

私有字段:使用 private 关键字定义,只有类内部可以访问。

受保护的字段:使用 protected 关键字定义,类和其子类可以访问。

公有字段:使用 public 关键字定义,任何地方都可以访问。

type TMyClass = class private FPrivateField: Integer; protected FProtectedField: Integer; public FPublicField: Integer; end; 

方法(Method)

方法是类中的函数或过程,用于定义对象的行为。方法可以是私有的、受保护的或公有的,与字段的访问级别类似。

type TMyClass = class public procedure ShowMessage; // 公有方法 end; procedure TMyClass.ShowMessage; begin Writeln('Hello, World!'); end; 

2.构造函数和析构函数

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 自定义构造函数
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.属性

属性是字段的抽象化,允许你使用类似于字段的语法访问类的私有数据,同时可以通过 gettersetter 方法控制访问。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



当你使用 Calc.Symbol 时,实际上是调用了 GetSymbolSetSymbol 方法,而不是直接访问 FSymbol

Calc.Symbol := '+'; 等同于 Calc.SetSymbol('+');,这会调用 SetSymbol 方法,将 + 赋值给 FSymbol

4.创建和销毁对象

在 Delphi 中,创建对象通常使用 Create 方法,而销毁对象则使用 Free 方法。

var MyObject: TMyClass; begin MyObject := TMyClass.Create; // 创建对象 try // 使用 MyObject finally MyObject.Free; // 销毁对象 end; end; 

Free 方法Free 方法检查对象是否为 nil,如果不是,则调用 Destroy 方法,并将对象指针设置为 nil

在这里插入图片描述

5.类的方法

5.1静态方法

关键字:无

默认情况下,如果方法没有使用 virtualdynamicoverride 声明,则它是一个静态方法。

调用静态方法时,编译器会直接将调用目标方法的地址嵌入到调用代码中,因此调用速度是最快的。

5.2虚拟方法

关键字:virtual

使用 virtual 关键字声明的方法是虚拟方法。虚拟方法表(VMT)用于支持多态性。

当调用虚拟方法时,编译器会通过 VMT 查找实际的方法地址,这允许子类重写该方法,并且调用总是指向最合适的子类实现。

虚拟方法的调用略慢于静态方法,因为它涉及一次额外的内存查找。

5.3动态方法

关键字:dynamic

dynamic 方法的作用类似于虚拟方法,但它使用动态方法表(DMT)而不是虚拟方法表(VMT)。

dynamic 方法的调用机制相对来说比虚拟方法更节省内存,因为 DMT 是一种稀疏数据结构,在子类不重写该方法时不会占用内存。

由于 DMT 的查找比 VMT 更复杂,所以 dynamic 方法的调用速度比 virtual 方法稍慢。

5.4类方法

关键字:class

在 Delphi 中,类方法(class methods) 具有类似于C++静态方法的行为。可以通过类名调用的方法,可以访问类的静态成员和类方法。

5.5抽象方法

关键字:abstract

在 Delphi 中,抽象方法是用来定义在基类中的方法,但基类不提供该方法的实现,而是留给派生类去实现。这种方法定义了一个接口,指定了派生类应该实现哪些方法。

在 Delphi 中,抽象方法使用 abstract 关键字。要定义一个抽象方法,你首先将方法声明为 virtualdynamic,然后在方法声明的末尾加上 abstract 关键字。抽象方法通常不提供具体的实现,只是在声明中描述它的接口。

一个包含抽象方法的类通常被称为抽象类。抽象类不能被实例化,因为它无法提供对抽象方法的具体实现。只有在派生类中实现了所有抽象方法后,才能实例化该派生类。

在这里插入图片描述

5.6消息方法

使用 message 关键字的方法用于处理 Windows 消息(如窗口事件)。这类方法使用消息映射机制来处理方法调用。

静态方法(static:不能访问类的实例成员,只能访问静态成员或全局成员。

class procedure TMyClass.StaticMethod; static; begin Writeln('Static method called'); end; 

类方法(class:可以通过类名调用的方法,可以访问类的静态成员和类方法。

class procedure TMyClass.ClassMethod; begin Writeln('Class method called'); end; 

在这里插入图片描述

6.继承

6.1构造

在 Delphi 中,当继承了一个类并实现子类的构造函数时,需要显式调用父类的构造函数。

如果不这样做,Delphi 不会自动调用父类的构造函数,这与 C++的行为有所不同。

在这里插入图片描述

父类:

在这里插入图片描述
在这里插入图片描述

子类:

在这里插入图片描述

  • 显式使用 inherited 关键字来调用父类构造函数。

在这里插入图片描述

增加一个构造函数用来初始化继承的父类属性。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

  • 重写父类方法
    在这里插入图片描述
    在这里插入图片描述

6.2访问权限

  • 父类public、protected,子类正常访问。

在这里插入图片描述

  • 父类private,子类无法访问。

在这里插入图片描述
在这里插入图片描述

6.3多态

派生类必须加override指明该方法是重写父类的动态方法。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


  • 向上转型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在 Delphi 中,asis 是两个用于类型转换和类型检查的关键字。它们在处理对象和接口类型时非常有用,尤其是在涉及多态性和接口的场景中。

is 关键字

is 用于检查一个对象实例是否属于某个特定的类或其子类。它返回一个布尔值 (TrueFalse),表示对象是否是指定类型的实例或其派生类型的实例。

as 关键字

as 用于将对象转换为特定的类或接口类型。它尝试将对象转换为指定类型,并返回该类型的实例。如果转换失败(即对象不是指定类型的实例),则会引发 EInvalidCast 异常。

在这里插入图片描述

7.类的事件

事件是一种非常灵活的机制,可以允许类在发生某个操作时通知其他对象或代码执行。事件背后的机制是方法指针,你可以将一个方法赋值给事件处理程序,并在特定时机调用该事件。

定义类的事件

在一个类中定义事件,通常步骤如下:

  1. 声明事件类型。
  2. 声明一个事件变量。
  3. 在类的适当时机调用该事件。

image-20240910155534020

image-20240910160639254

image-20240910160657762

image-20240910160721155

image-20240910160735943

image-20240910160750981

八、接口

在 Delphi 中,接口(Interface)是用来定义一个契约或协议的,它描述了一个类应该具备的功能,而不涉及具体的实现。接口与类不同,它不包含任何数据成员或已实现的方法,而是只包含方法的声明。这些方法必须在实现接口的类中提供具体实现。

Delphi 中接口的特点

  1. 纯虚类:接口类似于一个纯虚类。它不能直接实例化,而是通过实现接口的类来实现接口中的方法。
  2. 引用计数:Delphi 中的接口具有自动引用计数的特点,这意味着接口会自动管理内存,减少内存泄漏的风险。当接口的引用计数达到零时,系统会自动释放内存。
  3. 多重继承:Delphi 中的类不支持多重继承,但接口支持多重实现。这意味着一个类可以实现多个接口,从而实现不同的功能。
  4. 接口与类的关系:在 Delphi 中,接口与类的关系类似于一种契约。实现某个接口的类必须提供接口中声明的所有方法的实现。
  5. GUID:每个接口都可以(但不是必须)与一个全球唯一标识符(GUID)关联。GUID 用于在运行时标识接口,特别是在 COM(组件对象模型)编程中。

如果不提供接口的方法的具体实现就会编译出错。

在这里插入图片描述
在这里插入图片描述

  • 一个类实现多个接口

在这里插入图片描述

什么时候用接口?

是否使用接口,通常取决于以下几个因素:

1. 需要定义一个契约或协议时

接口可以定义一个契约或协议,规定实现该接口的类必须提供哪些方法。这在以下情况中特别有用:

  • 多态性:当你希望对一组不同的类进行相同的操作时,可以使用接口。例如,你可能有多个不同的类

    (如 TDogTCatTBird),但它们都实现了 IAnimal 接口,这样你可以在不关心具

    体类的情况下调用 Speak 方法。

  • 插件架构:在构建可扩展的应用程序时,接口可以定义插件的标准。插件只需要实现接口,即可被主程序调用。

2. 需要实现松耦合时

接口有助于降低类之间的耦合度。通过接口,类只需知道接口的定义,而不需要了解具体的实现。这样,当实现发生变化时,对接口的依赖不会受到影响。

  • 依赖倒置原则:接口允许你将高层模块与低层模块解耦合,高层模块依赖于抽象(接口),而不是具

    体实现。这是依赖倒置原则(Dependency Inversion Principle)的核心思想。

3. 支持多重继承时

Delphi 不支持类的多重继承,但一个类可以实现多个接口。这允许你在同一个类中实现多个不同的契约,这在需要类具有多种不同的行为时非常有用。

  • 多角色对象:一个类可能需要在不同的上下文中扮演不同的角色。例如,一个对象可能既是一个

    IUser,也是一个 IManager。通过接口可以轻松实现这种情况。

4. 替换复杂的继承结构时

当你发现类的继承结构变得过于复杂时,使用接口可能是一个更好的选择。接口可以帮助你摆脱深度继承树,转而使用组合(Composition)来代替继承。

  • 避免继承的复杂性:继承带来的复杂性可能会导致代码难以理解和维护。接口通过将行为分离为不同的

    契约,可以简化类的设计。

5. 需要不同类的对象有相似的行为时

在设计时,可能会有多个不同的类,它们没有共同的父类,但是它们的某些行为是相似的。此时,可以通过接口来统一这些行为,使得它们可以被相同的代码处理。

  • 统一操作:例如,TPrinterTScreen 类可能都需要实现 IDrawable 接口,使得它

    们可以被同一个绘图操作所处理。

6. 需要第三方或插件扩展时

接口在设计第三方扩展或插件系统时非常重要。通过接口,主应用程序可以定义插件需要实现的功能,而插件开发者只需实现这些接口即可。

  • 扩展性:例如,设计一个支持不同支付方式的电商平台,您可以定义一个

    IPaymentProcessor 接口,支持的支付方式只需要实现这个接口即可被主程序调用。

7. 接口与抽象类的对比

在某些情况下,您可能会考虑是使用接口还是抽象类。一般来说:

  • 接口:用于定义行为的契约,没有任何实现细节。
  • 抽象类:用于在同一个类层次结构中共享代码,并且可以包含部分实现。

九、泛型容器

十、线程

假如我们有一段执行运算的代码,同时想要动态地将每次运算结果更新到UI中,如果没有使用多线程,就会出现界面卡死的情况,直到运算执行完毕,UI直接显示最终结果。

image-20240919104624107

因此我们创建一个线程类,在构造函数中传入TLabel控件。

image-20240919144913653

image-20240919144934448

image-20240919144844050

1.如何暂停和继续执行线程

关于Tevent的构造函数。

constructor TEvent.Create( EventAttributes: PSecurityAttributes; ManualReset,InitialState: Boolean; const Name: string; UseCOMWait: Boolean ); 

参数详解:

  1. AEventAttributes (nil)
    • 类型:PSecurityAttributes
    • 说明:这是一个指向 PSecurityAttributes 的指针,用于定义事件的安全属性。在一般情况下,这个参数可以设置为 nil,表示使用默认的安全属性。
  2. ManualReset (True)
    • 类型:Boolean
    • 说明:指定事件对象的重置模式。
      • True 表示手动重置事件。当事件被设为有信号状态后,必须手动调用 ResetEvent 方法将其重置为无信号状态。此模式适用于当一个或多个线程需要等待某一条件时使用。
      • False 表示自动重置事件。当事件被设为有信号状态后,系统会自动将其重置为无信号状态,且仅会释放一个等待线程。适用于只需要唤醒一个等待线程的场景。
  3. InitialState (False)
    • 类型:Boolean
    • 说明:设置事件的初始状态。
      • True 表示事件初始为有信号状态。
      • False 表示事件初始为无信号状态。此状态下,调用 WaitFor 方法的线程将被阻塞,直到事件状态被设置为有信号。
  4. Name (‘’)
    • 类型:string
    • 说明:指定事件对象的名称。通过名称,可以在不同进程间共享事件对象。
      • 如果设置为 ''(空字符串),表示事件对象不具备名称,因此它只能在当前进程中使用。
      • 可以指定一个名称,这样在进程间通信中,可以使用相同的名称来访问这个事件对象。

image-20240919150844986

image-20240919150914621

image-20240919150933354

2.终止线程

使用 Terminate 方法来控制线程终止是 Delphi 的标准做法

不要在设置了 FreeOnTerminate := True 的情况下手动调用 Free

  • 如果你将 FreeOnTerminate 设置为 True,就不需要手动释放线程对象,因为它会在终止时自动释放。

image-20240919153546003

image-20240919153600031

image-20240919153610109

image-20240919153618966

3.线程同步

首先这里创建了一个线程类,进行卖票,从第一张卖到第十张。

image-20240919164826837

image-20240919164832374

只有一个线程执行的结果如下:

image-20240919164855838

如果有多个线程执行,结果就会混乱并且没有唯一性:

image-20240919164945740

此时就需要使用线程同步的方法,避免出现多线程争用同一资源导致的错误。

3.1 Synchronize

Synchronize 是 Delphi TThread 类提供的一个方法,用于在主线程(UI 线程)中执行某些代码。由于 VCL(Visual Component Library)控件并不是线程安全的,所以我们不能直接在工作线程中操作它们,否则可能导致不可预期的行为或崩溃。Synchronize 解决了这个问题,确保在主线程中执行对 VCL 控件的操作。

image-20240919165323011

Execute 方法中使用了 Synchronize,这会导致 DoWork 方法在主线程中执行。由于 Synchronize 会阻塞工作线程,直到主线程执行完该方法,所以即使创建了多个线程,它们也不会同时执行 DoWork。当然这种方法不太适用于临界区的情况,一般是用来更新UI的。

3.2 TCriticalSection

用途TCriticalSection 是一种轻量级的同步机制,用于保护代码块或数据结构,使得在同一时刻只有一个线程可以访问它。适用于同一进程中的线程同步。

原理TCriticalSection 使用进入/离开(Enter/Leave)方法保护共享资源。一个线程在进入临界区时会锁定它,其他试图进入的线程将被阻塞,直到临界区被释放。

image-20240919170110403

image-20240919170043765

image-20240919170122715

注意事项:

如果还想要在 mmo1 中显示信息,因为 DoWork 现在是在工作线程中执行的,应该将 Form1.mmo1.Lines.Add(...) 这部分代码放在 Synchronize 中,这样可以保证对 VCL 控件的操作是安全的。

image-20240919170138702

3.3 TMutex

用途TMutex 用于多线程和多进程同步。它可以在不同的进程间共享,以确保只有一个线程或进程可以访问共享资源。

原理TMutex 基于操作系统提供的互斥量实现。线程通过请求和释放互斥量来控制对资源的访问。

优点:可以在不同进程之间同步。

缺点:比 TCriticalSection 开销更大,使用系统资源。

constructor TMutex.Create( MutexAttributes: PSecurityAttributes; InitialOwner: Boolean; const Name: string; UseCOMWait: Boolean = False ); 

MutexAttributes: 一个指向 SECURITY_ATTRIBUTES 结构的指针,定义了互斥对象的安全属性。在多数情况下,你可以传入 nil,表示使用默认的安全属性。

InitialOwner: 一个布尔值,如果为 True,则调用线程在创建互斥对象时立即拥有它。如果为 False,则互斥对象创建后处于未拥有状态。

Name: 互斥对象的名称,可以用来在不同进程之间共享。如果传入空字符串,则创建一个匿名互斥对象。

UseCOMWait: 同上,指定是否允许在等待期间处理消息和 COM 调用。

image-20240919171306890

image-20240919171257866

image-20240919171319364

3.4 TEvent

1.如何暂停和继续执行线程 这里说过

3.5 TMonitor

用途TMonitor 是 Delphi 提供的高级同步机制,用于简化线程同步。它为对象提供了进入/离开临界区的功能,并且支持条件变量的等待和通知。

原理TMonitor 内部维护了一个临界区(类似于 TCriticalSection)和条件变量(类似于 TEvent),允许线程等待某个条件,并在满足条件时通知等待的线程。

TMonitor 的主要方法

  • Enter / Exit: Enter 方法用于获取锁,Exit 方法用于释放锁。这两个方法通常成对使用,确保锁在获得后会被释放。
  • TryEnter: 尝试获取锁,如果锁被其他线程持有,TryEnter 会立即返回 False,否则返回 True 表示成功获取锁。
  • Wait: 在当前线程中等待,直到收到来自其他线程的通知。Wait 会释放当前锁定并进入等待状态,直到 SignalPulse 方法被调用。
  • Pulse: 唤醒等待线程中的一个。
  • PulseAll: 唤醒所有等待线程。

image-20240919172112940

image-20240919172121209

image-20240919172137977

image-20240919172149453

4.生产者消费者问题

十一、DLL

1.动态调用

image-20240920193046328

image-20240920193100981

image-20240920193112591

image-20240920193818986

image-20240920193925038

2.静态调用

image-20240920194147860

image-20240920194129936

3.DLL的初始化和退出

Delphi 可以通过 DllProc 来处理加载和卸载事件。通过将 DllProc 设置为回调函数,处理 DLL 的加载(DLL_PROCESS_ATTACH)和卸载(DLL_PROCESS_DETACH)事件。

image-20240920204324081

image-20240920204340335

image-20240920204431301

image-20240920202443871

image-20240920204508269

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/139796.html

(0)
上一篇 2025-06-01 21:26
下一篇 2025-06-01 21:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信