在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
【详解】Delphi多线程处理 http://hi.baidu.com/boxroom/blog/item/97a82a3fea4606e554e7239f.html 1-1 多线程的基本概念 多线程带来如下好处:(自己阅读) 多线程的两个概念: 1)进程:也称任务,程序载入内存,并分配资源,称为“一个进程”。 2)线程:是程序的执行单位(线程本身并不包括程序代码,真正拥有代码的是进程),每个进程至少包括一个线程,称为主线程,一个进程如果有多个线程,就可以共享同一进程的资源,并可以并发执行。 请注意: 1-2 Tthread 对象 虽然Windows 提供了比较多的多线程设计的API 函数,但是直接使用API 函数一方面极其不方便,而且使用不当还容易出错。为解决这个问题,Borland 公司率先推出了一种Tthread 对象,来解决多线程设计上的困难,简化了多线程问题的处理。 一、Tthread 对象的主要方法 构造线程: constructor Create(CreateSuspended:boolean) 其中:CreateSuspended=true 构造但不唤醒 也可以用如下方法 inheried Create(CreateSuspended:boolean) 挂起线程:suspend 唤醒线程: 析构(清除线程所占用的内存): 终止线程(后面会具体讨论): 二、线程应用的简单例子: 下面通过一个例子说明上述方法的应用。我们知道,循环是独占性最强的运行方式之一,现在希望建立两个线程对象,实现循环的并行运行。具体方法如下: File---New---Thread Object 这就自动在主Form中建立了一个线程单元(在对话框里写上线程名字),默认的名字是Unit2。同样方法建立第二个线程单元Unit3。 procedure Object.Execute; end; 其中的程序是线程唤醒后自动执行的程序,也可以在里面调用其他自定义的过程和函数。这个过程的结束,意味着线程程序的结束。 type private procedure Execute; override; 并且在implementation区域写上: constructor Object.create; 假定我们给两个线程对象起的名字是: 这样在Unit1,应该作如下声明: {$R *.DFM} uses unit2,unit3; var thread1:mymath1; 这样在主线程,将可以通过这两个线程变量调用对应的线程方法。 在主线程区构造线程的方法是: 挂起: 唤醒: 析构: 这里需要说明的是,由于线程单元需要调用Form的Edit控件(对象),可以采用两种方法: 1)在线程单元定义一个TEdit对象,例如 在Execute过程内直接引用 但在Unit1中一定要在FormCreate过程里作一个赋值: procedure TForm1.FormCreate(Sender: TObject); 2)在第二个线程中首先声明调用Unti1,也就是要加上 这样就可以在该线程单元直接调用主Form的控件了,比如在Unit3中可以写: form1.edit2.text:=inttostr(i) 了解了这些基本规则,就可以写出比较复杂的多线程程序了。 Uses Classes; 这样,往往很多函数和对象在线程单元里不能使用,所以在必要时,应该根据需要User相应的单元,这个例程为了简单,把大部分常用的单元都拷过去了,这并不是推荐的办法,因为这样一来会使程序的垃圾过多,所以,一般要用什么拷什么。 三、常用的API 函数 在处理多线程问题的时候,也经常用到Windows提供的API 函数,需要说明的是,Tthread 对象内部封装的方法,其实主要也是调用API 函数,但是,考虑更全面,更安全。而直接调用API 函数,往往会因为运用不当,出现一些不应有的错误。所以,我个人以为,只要用Tthread 对象的方法能解决的,就不要直接调用API 函数,API 函数只应该在用在Tthread 对象方法解决不了的时候。 参数2,--线程堆栈尺寸(一般=0,与主线程相同长度,而且可以根据需要自动变化) 书上有这个函数应用的十分清晰的例子,可以自己阅读。 对应suspend(挂起)和resume(唤醒)的两个API 函数为: 其中,Thandle被要求控制线程的句柄,函数调用成功,返回挂起的次数,调用不成功。则返回0xFFFFFFFF。 四、线程的终止和退出: 1)自动退出: 一个线程从Execute()过程中退出,即意味着线程的终止,此时将调用Windows的ExitThread()函数来清除线程所占用的堆栈。 2)受控退出: 利用线程对象的Terminate属性,可以由进程或者由其他线程控制线程的退出。只需要简单的调用该线程的Terminate方法,并设直线程对象的Terminate属性为True。 While not Terminate do 3)退出的API 函数: 关于线程退出的API 函数声明如下:code Function TerminateThread(hThread:Thandle;dwExitCode:DWORD); 不过,这个函数会使代码立刻终止,而不管程序中有没有 4) 利用挂起线程的方法(suspend) 利用挂起线程的suspend方法,后面跟个Free,也可以释放线程,
在多线程的情况下,一般要根据线程执行任务的重要性,给线程适当的优先级,一般如果量的线程同时申请CPU 时间,优先级高的线程优先。 在Windows下,给线程的优先级分为30级,而Delphi中Tthread 对象相对简单的把优先级分为七级。也就是在Tthread中声明了一个枚举类型TTthreadPriority: type TTthreadPriority(tpidle,tpLowest,tpLower,tpNormal, 分别对应的是最低(系统空闲时有效,-15),较低(-2),低(-1),正常(普通0),高(1),较高(2),最高(15)。 其中tpidle和tpTimecrital有些特殊,具体情况请阅读书上有关内容。 设置优先级可使用thread对象的priority属性: 这里给出了一个演示多线程优先级的实例:
一)使用ADO模式 由于Delphi 6.0的ADO 数据源控件内置了多线程能力,所以,在ADO模式下,使用多线程不需要做更多的工作。用两个ADOTable控件,分别连到两个数据库,并且分别通过DataSource控件,与数据帮定控件联系就可以了,这样就可以实现前后台处理数据库问题。 二)使用BDE模式和Tseeion对象 如果需要使用BDE 模式,那么多线程使用数据库,就要考虑Session的问题。在单线程时,每个数据源的建立就自动生成一个Session,这是这个数据源私有的关于数据库信息的文件。但多线程时,必须统一管理,所以在BDE 中专门提供了一个Tsession对象,它可以同时管理不同的Databas数据源对象。 数据库1---databas(2)----table(Qurey)(3)---datasource
2-4 多线程的同步机制 同步机制,实际上是事件驱动机制,意思是让线程平时处于“休眠”状态,除非发生某个事件才触发。 一、使用Synchronize方法 这个方法用于访问VCL 主线程所管理的资源,其方法的应用是: procedure Theater.update; 这里通过 Synchronize使线程方法update同步。
在Delphi的IDE提供的构件中,有一些对象内部提供了线程的同步机制,工作线程可以直接使用这些控件,比如:Tfont,Tpen,TBitmap,TMetafile,Ticon等。另外,一个很重要的控件对象叫TCanvas,提供了一个Lock方法用于线程的同步,当一个线程使用此控件对象的时候,首先调用这个对象的Lock方法,然后对这个控件进行操作,完毕后再调用Unlock方法,释放对控间的控制权。 三、Waitfor方法 Function Waitfor(Const Astring:string):string; thread1.resume; 那么所有的线程都必须等待thread1运行完毕后才能运行,其中包括主线程,可以预想,由于thread1调用了主窗体的Edit控件,那么,在thread1运行中间,Edie1也不会显示。
Windows API函数提供了很多同步技术,下面简要介绍。 1)临界区 var 初始化: 独占 解除独占 使用临界区是比较方便而且概念比较清晰的的线程同步机制,应用比较广泛。 ━━━━━━━━━━━━━━━━━━━━━━━━━━ |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论