delphi多线程,Delphi多线程 句柄无效 闪退

http://www.itjxue.com  2023-01-14 01:58  来源:未知  点击次数: 

delphi多线程互斥,用多线程怎么解决同一时间内调用同一函数

Delphi同步互斥总结

多个线程同时访问一个共享资源或数据时,需要考虑线程同步,Synchronize()是在一个隐蔽的窗口里运行,如果在这里你的任务很繁忙,你的主窗口会阻塞掉;Synchronize()只是将该线程的代码放到主线程中运行,并非线程同步。

临 界区是一个进程里的所有线程同步的最好办法,他不是系统级的,只是进程级的,也就是说他可能利用进程内的一些标志来保证该进程内的线程同步,据

Richter说是一个记数循环;临界区只能在同一进程内使用;临界区只能无限期等待,不过2k增加了TryEnterCriticalSection函

数实现0时间等待。 互斥则是保证多进程间的线程同步,他是利用系统内核对象来保证同步的。由于系统内核对象可以是有名字的,因此多个

进程间可以利用这个有名字的内核对象保证系统资源的线程安全性。互斥量是Win32

内核对象,由操作系统负责管理;互斥量可以使用WaitForSingleObject实现无限等待,0时间等待和任意时间等待。常见的线程同步方法如下:

1. 临界区

临界区是一种最直接的线程同步方式。所谓临界区,就是一次只能由一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。使用方法如下:

//在窗体创建中

InitializeCriticalSection(Critical1)

//在窗体销毁中

DeleteCriticalSection(Critical1)

//在线程中

EnterCriticalSection(Critical1)

……保护的代码

LeaveCriticalSection(Critical1)

2. 互斥

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

临界区与事件对象(比如互斥对象)的最大的区别是在性能上。临界区在没有线程冲突时,要用10 ~

15个时间片,而事件对象由于涉及到系统内核要用400~600个时间片。

Mutex(互斥对象),是用于串行化访问资源的全局对象。我们首先设置互斥对象,然后访问资源,最后释放互斥对象。在设置互斥对象时,如果另一个线程(或进程)试图设置相同的互斥对象,该线程将会停下来,直到前一个线程(或进程)释放该互斥对象为止。注意它可以由不同应用程序共享。使用方法如下:

//在窗体创建中

hMutex:=CreateMutex(nil,false,nil)

//在窗体销毁中

CloseHandle(hMutex)

//在线程中

WaitForSingleObject(hMutex,INFINITE)

……保护的代码

ReleaseMutex(hMutex)

3. 信号量

另一种使线程同步的技术是使用信号量对象。它是在互斥的基础上建立的,但信号量增加了资源计数的功能,预定数目的线程允许同时进入要同步的代码。可以用CreateSemaphore()来创建一个信号量对象,

因为只允许一个线程进入要同步的代码,所以信号量的最大计数值(lMaximumCount)要设为1。其实Mutex就是最大计数为一的Semaphore。使用方法如下:

//在窗体创建中

hSemaphore:= CreateSemaphore(nil,lInitialCount,lMaximumCount,lpName)

//在窗体销毁中

CloseHandle(hSemaphore)

//在线程中

WaitForSingleObject(hSemaphore,INFINITE)

……保护的代码

ReleaseSemaphore(hSemaphore, lReleaseCount, lpPreviousCount)

4.WaitForSingleObject函数的返值:

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

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

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

Delphi 常用的临界区对象TCriticalSection(Delphi) 、TRtlCriticalSection

TRtlCriticalSection 是一个结构体,在windows单元中定义;

是InitializeCriticalSection,EnterCriticalSection,LeaveCriticalSection,

DeleteCriticalSection 等这几个kernel32.dll中的临界区操作API的参数;

TCriticalSection是在SyncObjs单元中实现的类,它对上面的那些临界区操作API函数进行了了封装,简化并方便了在Delphi的使用;如TCriticalSection.Create,TCriticalSection.Enter,

TcriticalSection.Leave等;通过调用上面响应的API函数实现。

线程同步的多种办法中,使用临界区最简单,也是效率最高的办法(CPU占用时间最少)

使用临界区代码如下:

先声明一个TRTLCriticalSection类型的全局变量

var

MyCs:TRTLCriticalSection;

在程序开始或建立线程之前,初始化

InitializeCriticalSection(MyCs);//初始化临界区

在程序结束或所有线程结束后,删除它

DeleteCriticalSection(MyCs);//删除临界区

再在线程中要同步的地方加入

EnterCriticalSection(MyCs); //进入临界区

try

//程序代码

finally

LeaveCriticalSection(MyCs); //离开临界区

end;

补充今天遇到的关于Application.ProcessMessages同步的问题:有一个函数Fn按执行顺序可分为A-B-C

3大块,其中B块有要绘制各种窗口界面的操作很复杂且耗时较长,并且里面用到了Application.ProcessMessages,程序运行测试时发现如果在Fn执行B绘制窗口的过程没结束时又调用Fn函数去绘制其它窗口就可能会导致程序崩溃,一开始尝试用TcriticalSection变量解决,完全没用,最后用增加一个全局变量的方法解决:定义一个全局Boolean型变量flag,设定初始值为True,改造Fn函数的逻辑为A-

if flag then

Begin

Flag:=False;

B;

Flag:=True;

End;

-C

问题成功解决。

顺便总结Application.ProcessMessages的作用:运行一个非常耗时的循环,那么在这个循环结束前,程序可能不会响应任何事件,按钮没有反应,程序设置无法绘制窗体,看上去就如同死了一样,这有时不是很方便,例如于终止循环的机会都没有了,又不想使用多线程时,这时你就可以在循环中加上这么一句,每次程序运行到这句时,程序就会让系统响应一下消息,从而使你有机会按按钮,窗体有机会绘制。所起作用类似于VB中DoEvent方法.

调用ProcessMessages来使应用程序处于消息队列能够进行消息处理,ProcessMessages将Windows消息进行循环轮转,直至消息为空,然后将控制返回给应用程序。

注示:仅在应用程序调用ProcessMessages时勿略消息进程效果,而并非在其他应用程序中。在冗长的操作中,调用ProcessMessages周期性使得应用程序对画笔或其他信息产生回应。

ProcessMessages不充许应该程序空闲,而HandleMessage则然.使用ProcessMessages一定要保证相关代码是可重入的,如果实在不行也可按我上面的方法实现同步。

delphi 怎么实现多线程的同步

多线程同步

"临界区"(CriticalSection):?当把一段代码放入一个临界区,?线程执行到临界区时就独占了,?让其他也要执行此代码的线程先等等;

使用格式如下:

var?CS:?TRTLCriticalSection;???{声明一个?TRTLCriticalSection?结构类型变量;?它应该是全局的}?

InitializeCriticalSection(CS);?{初始化}?

EnterCriticalSection(CS);??????{开始:?轮到我了其他线程走开}?

LeaveCriticalSection(CS);??????{结束:?其他线程可以来了}?

DeleteCriticalSection(CS);?????{删除:?注意不能过早删除}?

多线程同步示例

?1?unit?Unit1;?

?2??

?3?interface?

?4??

?5?uses?

?6???Windows,?Messages,?SysUtils,?Variants,?Classes,?Graphics,?Controls,?Forms,?

?7???Dialogs,?StdCtrls;?

?8??

?9?type?

10???TForm1?=?class(TForm)?

11?????ListBox1:?TListBox;?

12?????Button1:?TButton;?

13?????procedure?FormCreate(Sender:?TObject);?

14?????procedure?FormDestroy(Sender:?TObject);?

15?????procedure?Button1Click(Sender:?TObject);?

16???end;?

17??

18?var?

19???Form1:?TForm1;?

20??

21?implementation?

22??

23?{$R?*.dfm}?

24??

25?var?

26???CS:?TRTLCriticalSection;?

27??

28?function?MyThreadFun(p:?Pointer):?DWORD;?stdcall;?

29?var?

30???i:?Integer;?

31?begin?

32???EnterCriticalSection(CS);?

33???for?i?:=?0?to?99?do?Form1.ListBox1.Items.Add(IntToStr(i));?

34???LeaveCriticalSection(CS);?

35???Result?:=?0;?

36?end;?

37??

38?procedure?TForm1.Button1Click(Sender:?TObject);?

39?var?

40???ID:?DWORD;?

41?begin?

42???CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);?

43???CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);?

44???CreateThread(nil,?0,?@MyThreadFun,?nil,?0,?ID);?

45?end;?

46??

47?procedure?TForm1.FormCreate(Sender:?TObject);?

48?begin?

49???ListBox1.Align?:=?alLeft;?

50???InitializeCriticalSection(CS);?

51?end;?

52??

53?procedure?TForm1.FormDestroy(Sender:?TObject);?

54?begin?

55???DeleteCriticalSection(CS);?

56?end;?

57??

58?end.

Delphi中如何多线程操作数据库中的数据表

1。通过线程的互斥来同步操作数据库 2。数据库采用事务处理表中的数据 3。采用共享方式打开数据库,不是以独占方式打开数据库 建立一个mysql连接表加上一个临界区,表结点是这样的(mysqlcon,bool),根据实际情况定大校我用的是10个连接。

Delphi 如何用多线程进行数据采集

就以下两个方面来讲解以下在delphi中如何用多线程进行数据采集:

---- 1. 多线程进行数据采集应解决的问题

---- 其实,多线程程序设计复杂是暂时的;如果,你采用传统的C进行多线程的设计,那么你必须自己控制线程间的同步。那将是很复杂的。但是,如果利用面向对象的设计方法,采用Delphi进行多线程程序设计,问题就简单多了。这是因为,Delphi已将多线程的复杂性替我们处理了,我们所要做的就是继承。

---- 具体地说,多线程数据采集需要完成以下工作:

---- ① 从TThread类派生一个自己的类SampleThread。这就是我们用于数据采集的类。进行采集时,只需要简单地创建一个SampleThread的实例。

---- ② 重载超类TThread的Execute方法。在这一方法中将具体地执行数据采集任务。

---- ③ 如果希望一边采集一边显示,就在编写几个用于显示采集进度的过程,供Execute方法调用。

---- TThread类中最常用的属性/方法如下:

Create方法:constructor Create

(CreateSuspended: Boolean);

---- 其中CreateSuspended参数确定线程在创建时是否立即执行。如果为True,新线程在创建后被挂起;如果为False,线程在创建后立即执行。

FreeOnTerminate属性:

property FreeOnTerminate: Boolean;

---- 该属性确定程序员是否负责撤消该线程。如果该属性为True,VCL将在该线程终止时自动撤消线程对象。它的缺省值为False。

OnTerminate属性:

property OnTerminate: TNotifyEvent;

---- 该属性指定一个当线程终止时发生的事件。

---- 下面看一个具体的例子:

---- 2. 多线程数据采集的实现

---- 这是笔者开发的一个测抽油机功图的程序。它的功能是采集抽油机悬点的载荷及位移数据,经过处理后做出抽油机的功图。图1(略)所示是数据采集时的界面。点“采集数据”按钮后,程序将创建一新的线程,并设置其属性。这一新线程将完成数据采集任务。程序如下:

Procedure TsampleForm.

DoSampleBtnClick(Sender: TObject);

Begin

ReDrawBtn.Enabled := True;

DoSampleBtn.Enabled := False;

FFTBtn.Enabled := True;

TheSampler := SampleThread.Create(False);

创建采集线程

TheSampler.OnTerminate := FFTBtnClick;

采集完成后要执行的任务

TheSampler.FreeOnTerminate := True;

采集完成后撤消

End;

---- 采集线程的类定义如下:

Type

SampleThread = class(TThread)

Public

function AdRead(ach: byte): integer; safecall;

读A/D卡的函数

procedure UpdateCaption;

显示采集所用时间

private

{ Private declarations }

protected

thes, thep: real;

dt: real;

id: integer;

st, ed: LongInt;

procedure Execute; override;

这是关键。

End;

---- 在这个类中定义了一个函数AdRead用于操作A/D卡,两个过程用于显示采集的进度与所用时间。需要注意的是AdRead函数是用汇编写的,参数调用格式必须是safecall。

---- 关键的重载方法Execute的代码如下:

Procedure SampleThread.Execute;

Begin

StartTicker := GetTickCount;

id := 0;

Repeat

thes := Adread(15) * ad2mv * mv2l;

采集第15通道

thep := Adread(3) * ad2mv * mv2n;

采集第3通道

dt := GetTickCount - StartTicker;

sarray[id] := thes;

parray[id] := thep;

tarray[id] := dt;

inc(id);

Synchronize(UpdateCaption);

注意:显示采集进度

Until id =4096;

ed := GetTickCount;

Synchronize(ShowCostTime);

注意:显示所用时间

end;

---- 从以上代码中可见,Execute与一般的代码并无本质区别。仅有的区别是显示采集进度和显示所用时间时,不能直接调用各自的过程,而是通过调用Synchronize间接地调用。这样作是为了保持进程间的同步。

delphi 多线程中如何得到线程句柄?

如果你的多线程是用TThread实现的,属性ThreadID可以得到指定线程的句柄如果你是通过BeginThread运行的线程函数,可以通过返回值ThreadID得到BeginThread(SecurityAttributes:

Pointer;

StackSize:

LongWord;

ThreadFunc:

TThreadFunc;

Parameter:

Pointer;

CreationFlags:

LongWord;

var

ThreadId:

LongWord):

Integer;如果你是通过CreateThread创建的,也可以通过返回值ThreadID得到HANDLE

CreateThread(

LPSECURITY_ATTRIBUTES

lpThreadAttributes,

//

pointer

to

thread

security

attributes

DWORD

dwStackSize,

//

initial

thread

stack

size,

in

bytes

LPTHREAD_START_ROUTINE

lpStartAddress,

//

pointer

to

thread

function

LPVOID

lpParameter,

//

argument

for

new

thread

DWORD

dwCreationFlags,

//

creation

flags

LPDWORD

lpThreadId

//

pointer

to

returned

thread

identifier

);

如果想得到当前运行的线程的句柄用楼上的方法

(责任编辑:IT教学网)

更多

推荐人物新闻文章