已阅读5页,还剩11页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
delphi多线程编程2在这段程序中, 有三个线程几乎是同时建立, 向窗体中的 ListBox1 中写数据, 最后写出的结果是这样的:能不能让它们别打架, 一个完了另一个再来? 这就要用到多线程的同步技术.前面说过, 最简单的同步手段就是 临界区.先说这个 同步(Synchronize), 首先这个名字起的不好, 我们好像需要的是 异步; 其实异步也不准确.管它叫什么名字呢, 它的目的就是保证不*、有次序、都发生.临界区(CriticalSection): 当把一段代码放入一个临界区, 线程执行到临界区时就独占了, 让其他也要执行此代码的线程先等等; 这和前面用的 Lock 和 UnLock 差不多; 使用格式如下:var CS: TRTLCriticalSection; 声明一个 TRTLCriticalSection 结构类型变量; 它应该是全局的InitializeCriticalSection(CS); 初始化EnterCriticalSection(CS);开始: 轮到我了其他线程走开LeaveCriticalSection(CS);结束: 其他线程可以来了DeleteCriticalSection(CS); 删除: 注意不能过早删除/也可用 TryEnterCriticalSection 替代 EnterCriticalSection.用上临界区, 重写上面的代码, 运行效果图:代码文件:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)ListBox1: TListBox;Button1: TButton;procedure FormCreate(Sender: TObject);procedure FormDestroy(Sender: TObject);procedure Button1Click(Sender: TObject);end;varForm1: TForm1;implementation$R *.dfmvarCS: TRTLCriticalSection;function MyThreadFun(p: Pointer): DWORD; stdcall;vari: Integer;beginEnterCriticalSection(CS);for i := 0 to 99 do Form1.ListBox1.Items.Add(IntToStr(i);LeaveCriticalSection(CS);Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);varID: DWORD;beginCreateThread(nil, 0, MyThreadFun, nil, 0, ID);CreateThread(nil, 0, MyThreadFun, nil, 0, ID);CreateThread(nil, 0, MyThreadFun, nil, 0, ID);end;procedure TForm1.FormCreate(Sender: TObject);beginListBox1.Align := alLeft;InitializeCriticalSection(CS);end;procedure TForm1.FormDestroy(Sender: TObject);beginDeleteCriticalSection(CS);end;end.Delphi 在 SyncObjs 单元给封装了一个 TCriticalSection 类, 用法差不多, 代码如下:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)ListBox1: TListBox;Button1: TButton;procedure FormCreate(Sender: TObject);procedure FormDestroy(Sender: TObject);procedure Button1Click(Sender: TObject);end;varForm1: TForm1;implementation$R *.dfmuses SyncObjs;varCS: TCriticalSection;function MyThreadFun(p: Pointer): DWORD; stdcall;vari: Integer;beginCS.Enter;for i := 0 to 99 do Form1.ListBox1.Items.Add(IntToStr(i);CS.Leave;Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);varID: DWORD;beginCreateThread(nil, 0, MyThreadFun, nil, 0, ID);CreateThread(nil, 0, MyThreadFun, nil, 0, ID);CreateThread(nil, 0, MyThreadFun, nil, 0, ID);end;procedure TForm1.FormCreate(Sender: TObject);beginListBox1.Align := alLeft;CS := TCriticalSection.Create;end;procedure TForm1.FormDestroy(Sender: TObject);beginCS.Free;end;end.多线程编程(9) - 认识等待函数 WaitForSingleObject。 一下子跳到等待函数 WaitForSingleObject, 是因为下面的Mutex、Semaphore、Event、WaitableTimer 等同步手段都要使用这个函数; 不过等待函数可不止WaitForSingleObject 它一个, 但它最简单.function WaitForSingleObject(hHandle: THandle;要等待的对象句柄dwMilliseconds: DWORD等待的时间, 单位是毫秒): DWORD; stdcall; 返回值如下:WAIT_OBJECT_0等着了, 本例中是: 等的那个进程终于结束了WAIT_TIMEOUT 等过了点(你指定的时间), 也没等着WAIT_ABANDONED 好不容易等着了, 但人家还是不让咱执行; 这一般是互斥对象/WaitForSingleObject 的第二个参数一般给常数值 INFINITE, 表示一直等下去, 死等.WaitForSingleObject 等待什么? 在多线程里就是等待另一个线程的结束, 快来执行自己的代码; 不过它可以等待的对象可不止线程; 这里先来一个等待另一个进程结束的例子, 运行效果图:代码文件:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)Button1: TButton;procedure Button1Click(Sender: TObject);end;varForm1: TForm1;implementation$R *.dfmvarhProcess: THandle; 进程句柄等待一个指定句柄的进程什么时候结束function MyThreadFun(p: Pointer): DWORD; stdcall;beginif WaitForSingleObject(hProcess, INFINITE) = WAIT_OBJECT_0 thenForm1.Text := Format(进程 %d 已关闭, hProcess);Result := 0;end;启动一个进程, 并建立新线程等待它的结束procedure TForm1.Button1Click(Sender: TObject);varpInfo: TProcessInformation;sInfo: TStartupInfo;Path: array0.MAX_PATH-1 of Char;ThreadID: DWORD;begin先获取记事本的路径GetSystemDirectory(Path, MAX_PATH);StrCat(Path, notepad.exe);用 CreateProcess 打开记事本并获取其进程句柄, 然后建立线程监视FillChar(sInfo, SizeOf(sInfo), 0);if CreateProcess(Path, nil, nil, nil, False, 0, nil, nil, sInfo, pInfo) thenbeginhProcess := pInfo.hProcess; 获取进程句柄Text := Format(进程 %d 已启动, hProcess);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID); 建立线程监视end;end;end.窗体文件:object Form1: TForm1Left = 0Top = 0Caption = Form1ClientHeight = 124ClientWidth = 241Color = clBtnFaceFont.Charset = DEFAULT_CHARSETFont.Color = clWindowTextFont.Height = -11Font.Name = TahomaFont.Style = OldCreateOrder = FalsePixelsPerInch = 96TextHeight = 13object Button1: TButtonLeft = 88Top = 56Width = 75Height = 25Caption = Button1TabOrder = 0OnClick = Button1Clickendend多线程编程(10) - 多线程同步之 Mutex (互斥对象)。 原理分析:互斥对象是系统内核对象, 各线程都可以拥有它, 谁拥有谁就能执行;执行完毕, 用 ReleaseMutex 函数释放拥有权, 以让其他等待的线程使用.其他线程可用 WaitForSingleObject 函数排队等候(等候也可以理解为排队申请).使用过程:var hMutex: THandle; 应该先声明一个全局的互斥句柄CreateMutex建立一个互斥对象WaitForSingleObject用等待函数排队等候ReleaseMutex 释放拥有权CloseHandle最后释放互斥对象ReleaseMutex、CloseHandle 的参数都是 CreateMutex 返回的句柄, 关键是 CreateMutex 函数:function CreateMutex(lpMutexAttributes: PSecurityAttributes;bInitialOwner: BOOL; 是否让创建者(此例中是主线程)拥有该互斥对象lpName: PWideChar可以给此互斥对象取个名字, 如果不要名字可赋值为 nil): THandle;1、第一个参数前面说过.2、第二个参数在这里一定要是 False, 如果让主线程拥有互斥, 从理论上讲, 得等程序退出后其他线程才有机会; 取值 False 时, 第一个执行的线程将会最先拥有互斥对象, 一旦拥有其他线程就得先等等.3、第三个参数, 如果给个名字, 函数将从系统中寻找是否有重名的互斥对象, 如果有则返回同名对象的存在的句柄; 如果赋值为 nil 将直接创建一个新的互斥对象; 下个例子将会有名字. 本例效果图:代码文件:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)Button1: TButton;procedure Button1Click(Sender: TObject);procedure FormCreate(Sender: TObject);procedure FormDestroy(Sender: TObject);end;varForm1: TForm1;implementation$R *.dfmvarf: Integer;用这个变量协调一下各线程输出的位置hMutex: THandle; 互斥对象的句柄function MyThreadFun(p: Pointer): DWORD; stdcall;vari,y: Integer;beginInc(f);y := 20 * f;for i := 0 to 50000 dobeginif WaitForSingleObject(hMutex, INFINITE) = WAIT_OBJECT_0 thenbeginForm1.Canvas.Lock;Form1.Canvas.TextOut(20, y, IntToStr(i);Form1.Canvas.Unlock;Sleep(0); 稍稍耽搁一点, 不然有时 Canvas 会协调不过来ReleaseMutex(hMutex);end;end;Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);varThreadID: DWORD;beginRepaint;f := 0;CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);end;procedure TForm1.FormCreate(Sender: TObject);beginhMutex := CreateMutex(nil, False, nil);end;procedure TForm1.FormDestroy(Sender: TObject);beginCloseHandle(hMutex);end;end.窗体文件:object Form1: TForm1Left = 0Top = 0Caption = Form1ClientHeight = 140ClientWidth = 192Color = clBtnFaceFont.Charset = DEFAULT_CHARSETFont.Color = clWindowTextFont.Height = -11Font.Name = TahomaFont.Style = OldCreateOrder = FalseOnCreate = FormCreatePixelsPerInch = 96TextHeight = 13object Button1: TButtonLeft = 109Top = 107Width = 75Height = 25Caption = Button1TabOrder = 0OnClick = Button1ClickendendSyncObjs 单元下有封装好的 TMutex 类, 好像不如 Api 快, 内部机制也稍有区别, 但使用方法差不多:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)Button1: TButton;procedure Button1Click(Sender: TObject);procedure FormCreate(Sender: TObject);procedure FormDestroy(Sender: TObject);end;varForm1: TForm1;implementation$R *.dfmuses SyncObjs;varf: Integer;MyMutex: TMutex;function MyThreadFun(p: Pointer): DWORD; stdcall;vari,y: Integer;beginInc(f);y := 20 * f;for i := 0 to 50000 dobeginif MyMutex.WaitFor(INFINITE) = wrSignaled thenbeginForm1.Canvas.Lock;Form1.Canvas.TextOut(20, y, IntToStr(i);Form1.Canvas.Unlock;MyMutex.Release;end;end;Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);varThreadID: DWORD;beginRepaint;f := 0;CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);CreateThread(nil, 0, MyThreadFun, nil, 0, ThreadID);end;procedure TForm1.FormCreate(Sender: TObject);beginMyMutex := TMutex.Create(False);end;procedure TForm1.FormDestroy(Sender: TObject);beginMyMutex.Free;end;end.多线程编程(11) - 多线程同步之 Mutex (互斥对象)续。Mutex 作为系统核心对象是可以跨进程的(临界区就不行), 我们可以利用互斥对象禁止程序重复启动.工作思路:先用 OpenMutex 尝试打开一个自定义名称的 Mutex 对象, 如果打开失败说明之前没有这个对象存在;如果之前没有这个对象, 马上用 CreateMutex 建立一个, 此时的程序应该是第一次启动;再重复启动时, 那个 OpenMutex 就有结果了, 然后强制退出.最后在程序结束时用 CloseHandle 释放 Mutex 对象.function OpenMutex(dwDesiredAccess: DWORD; 打开权限bInheritHandle: BOOL; 能否被当前程序创建的进程继承pName: PWideCharMutex 对象的名称): THandle; stdcall;成功返回 Mutex 的句柄; 失败返回 0注意, 这里的 CreateMutex 函数应该有个名了, 因为 OpenMutex 要用到;另外, CreateMutex 的第二个参数已经不重要了(也就是 True 和 False 都行), 因为这里是用其名称来判断的.程序可以这样写:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs;typeTForm1 = class(TForm)procedure FormCreate(Sender: TObject);procedure FormDestroy(Sender: TObject);end;varForm1: TForm1;implementation$R *.dfmvarhMutex: THandle;constNameMutex = MyMutex;procedure TForm1.FormCreate(Sender: TObject);beginif OpenMutex(MUTEX_ALL_ACCESS, False, NameMutex) 0 thenbeginShowMessage(该程序已启动);Application.Terminate;end;hMutex := CreateMutex(nil, False, NameMutex);end;procedure TForm1.FormDestroy(Sender: TObject);beginCloseHandle(hMutex);end;end.这一般都是写在 dpr 主程序里, 省得让后启动的程序执行些无用的代码:program Project1;usesForms, Windows,Unit1 in Unit1.pas Form1;$R *.resvarhMutex: THandle;constNameMutex = MyMutex;begin主线程入口if OpenMutex(MUTEX_ALL_ACCESS, False, NameMutex) 0 thenbeginMessageBox(0, 该程序已启动, 提示, MB_OK);Application.Terminate;end;hMutex := CreateMutex(nil, False, NameMutex);Application.Initialize;Application.MainFormOnTaskbar := True;Application.CreateForm(TForm1, Form1);Application.Run;CloseHandle(hMutex);主线程出口end.多线程编程(12) - 多线程同步之 Semaphore (信号对象)。之前已经有了两种多线程的同步方法:CriticalSection(临界区) 和 Mutex(互斥), 这两种同步方法差不多, 只是作用域不同;CriticalSection(临界区) 类似于只有一个蹲位的公共厕所, 只能一个个地进;Mutex(互斥) 对象类似于接力赛中的接力棒, 某一时刻只能一个人持有, 谁拿着谁跑.什么是 Semaphore(信号或叫信号量)呢?譬如到银行办业务、或者到车站买票, 原来只有一个服务员, 不管有多少人排队等候, 业务只能一个个地来.假如增加了业务窗口, 可以同时受理几个业务呢?这就类似与 Semaphore 对象, Semaphore 可以同时处理等待函数(如: WaitForSingleObject)申请的几个线程.Semaphore 的工作思路如下:1、首先要通过 CreateSemaphore(安全设置, 初始信号数, 信号总数, 信号名称) 建立信号对象;参数四: 和 Mutex 一样, 它可以有个名称, 也可以没有, 本例就没有要名称(nil); 有名称的一般用于跨进程.参数三: 信号总数, 是 Semaphore 最大处理能力, 就像银行一共有多少个业务窗口一样;参数二: 初始信号数, 这就像银行的业务窗口很多, 但打开了几个可不一定, 如果没打开和没有一样;参数一: 安全设置和前面一样, 使用默认(nil)即可.2、要接受 Semaphore 服务(或叫协调)的线程, 同样需要用等待函数(如: WaitForSingleObject)排队等候;3、当一个线程使用完一个信号, 应该用 ReleaseSemaphore(信号句柄, 1, nil) 让出可用信号给其他线程;参数三: 一般是 nil, 如果给个数字指针, 可以接受到此时(之前)总共闲置多少个信号;参数二: 一般是 1, 表示增加一个可用信号;如果要增加 CreateSemaphore 时的初始信号, 也可以通过 ReleaseSemaphore.4、最后, 作为系统内核对象, 要用 CloseHandle 关闭.另外, 在 Semaphore 的总数是 1 的情况下, 就和 Mutex(互斥) 一样了.在本例中, 每点击按钮, 将建立一个信号总数为 5 的信号对象, 初始信号来自 Edit1; 同时有 5 个线程去排队.本例也附上了 Delphi 中 TSemaphore 类的例子, 但没有过多地纠缠于细节, 是为了尽快理出多线程的整体思路.本例效果图:代码文件:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)Button1: TButton;Edit1: TEdit;procedure Button1Click(Sender: TObject);procedure FormCreate(Sender: TObject);procedure FormDestroy(Sender: TObject);procedure Edit1KeyPress(Sender: TObject; var Key: Char);en
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年社区网格员笔试题(附答案)
- 2025年社工考试题及答案
- 2025年平度市公益性岗位招聘真题
- 2024年贵港市法院招聘聘用制书记员笔试真题
- 万题库章节课护理基础学及答案解析
- 建筑工程进度款催收函范本
- 校园火灾应急疏散与逃生方案设计
- 工业园区消防演练与培训方案
- 校园消防安全联合应急演练方案
- 工控设备项目可行性分析报告范文(总投资15000万元)
- 老年骨科患者围手术期风险因素评估
- 2025医院财务管理制度
- 难点解析山东省邹城市7年级上册期中测试卷专题测试试卷(解析版)
- 屋顶分布式光伏发电项目施工组织设计
- 农业无人机打药培训课件
- 《氢燃料电池货车碳减排项目方法学(JXPHCER-03-004-V01)》
- 医院传染病预防培训体系
- 头晕鉴别诊断
- 脓毒症相关炎症标志物急诊应用专家共识解读课件
- 八年级上名著《红岩》第3章(讲练测)
- 内燃机在用润滑油品质现场检验法编制说明
评论
0/150
提交评论