一、Delphi线程对象--- Tthread 虽然Windows提供了较多的多线程设计的API 函数,但是直接使用API 函数极其不方便,而且使用不当还容易出错。为解决这个问题,Borland公司率先推出了一种Tthread 对象,来解决多线程设计上的困难,简化了多线程问题的处理。 一、Tthread对象的主要方法 构造线程: constructor Create(CreateSuspended:boolean) CreateSuspended=true构造但不唤醒 ;false构造的同时即唤醒 。 挂起线程:
终止线程 使用这个类也很简单,基本用法是:先从TThread派生一个自己的线程类(因为TThread 线程的终止和退出: 1)自动退出: 一个线程从Execute()过程中退出,即意味着线程的终止,此时将调用Windows的ExitThread()函数来清除线程所占用的堆栈。 如果线程对象的 FreeOnTerminate属性设为True,则线程对象将自动删除,并释放线程所占用的资源。 2)受控退出: 利用线程对象的Terminate属性,可以由进程或者由其他线程控制线程的退出。只需要简单的调用该线程的Terminate方法,并设置线程对象的Terminate属性为True。 3)退出的API函数: 关于线程退出的API函数声明如下: Function TerminateThread(hThread:Thandle;dwExitCode:DWORD); 不过,这个函数会使代码立刻终止,而不管程序中有没有 try....finally 机制,可能会导致错误,不到万不得已,最好不要使用。 4)利用挂起线程的方法(suspend) 利用挂起线程的suspend方法,后面跟个Free,也可以释放线程, thread2.free; //释放 二、多线程的同步机制 同步机制,研究多线程的同步机制的必要性在于,多线程同步工作时,如果同时调用相同的资源,就可能会出现问题,如对全局变量、数据库操作发生冲突,甚至产生死锁和竞争问题。 举个发生冲突的实例看一下: 一般来说,对内存数据加一的操作分解以后有三个步骤: 1.临界区(Critical Sections) 临界区(CriticalSection)是一项共享数据访问保护的技术。对它只有两个操作:Enter和Leave,这两个操作也是原子操作。 它 的保护原理是这样的:当一个线程A调用某一个Enter后,开始访问某个数据D,如果此时另一个线程B也要访问数据D,则它会在调用这个Enter时,发 现已经有线程进入临界区,然后线程B就会被挂起,等待线程A调用Leave。当线程A完成操作,调用Leave离开后,线程B就会被唤醒,并设置临界区标 志,开始操作数据,这样就防止了访问冲突 var Procedure InterlockedIncrement( var aValue : Integer ); 现在再来看前面那个例子: 请注意,临界区只能在一个进程内使用,可以在多处设置调用enter。 不要长时间锁住一份资源,如果你一直让资源被锁定,你就会阻止其它线程的执行,并把整个程序带到一个完全停止的状态,所以千万不要在一个ciritical section中调用sleep()或任何Wait…()函数。 ciritical section的一个缺点是,它不是核心对象,如果进入ciritical section的那个线程结束了或者当掉了,而没有调用LeaveCriticalSection的话,系统没有办法将该ciritical section清除,如果你需要这样的机能,你应该使用mutex。 VOID InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection//一个指针,指向欲被初始化的CRITICAL_SECTION变量 ); 函数功能:初始化一个临界对象,当你用毕临界对象时,必须调用DeleteCriticalSection()清除它。 VOID DeleteCriticalSection ( 函数功能:申请删除临界对象 VOID EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection//临界对象指针 ); 函数功能:申请进入临界对象 VOID LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection//临界对象指针 ); 函数功能 申请进入临界对象 2.互斥器(Mutexes) 一个时间内只能够有一个线程拥有mutex,就好像同一个时间只能够有一个线程进入同一临界区。 Mutex和critical section还是有差别的: 1.锁住一个未被使用的Mutexes,比锁住一个未被使用的critical section,需要花费几乎100倍的时间 2. Mutexes可以跨进程使用,critical section则只能够在同一个进程中使用 3.等待一个Mutexes时,你可以指定结束等待的时间长度,但对于critical section则不行。 两种对象的相关函数比较:
Mutex的使用机制: 1. 有一个mutex,此时没有任何线程拥有它,此时它处于非激发状态。 2. 某个线程调用WaitforSingleObject()或任何其它的wait…函数,并指定该mutex的handle为参数 3. win32于是将该mutex的拥有权给予这个线程,然后将此mutex设为激发状态,于是wait..函数返回 4. wait..函数返回后,win32立刻又将mutex设为非激发状态,是任何处于等待状态下的其它线程没有办法获得其拥有权 5. 获得该mutex的线程调用Release,将mutex释放掉。于是循环到第一步。 如 果线程拥有一个mutex,而在结束前没有调用releaseMutex,mutex不会被摧毁,该mutex会被win32视为“未被拥有”以及“未被 激发”,下一个等待中的线程会被以WAIT_ABANDONED_0通知。如果是WaitForMultipleObjects()等待辞mutex,函 数返回值介于WAIT_ABANDONED_0和WAIT_ABANDONED_0+n之间,n是指handle数组的元素个数。 HANDLE CreateMutex( 参数 lpMutexAttributes:安全属性。Null表示使用默认的属性。 bInitialOwner:如果你希望调用这个函数的线程拥有mutex,就将此值设为true lpName:互斥对象的名称 返回值 如果成功,则返回一个handle,否则返回null。 函数说明: 如 果指定的mutex名称已经存在,win32会给你一个mutex handle,而不会为你产生一个新的mutex。调用GetLastError会传回ERROR_ALREADY_EXISTS。当你不需要mutex 时,你可以调用closehandle()将它关闭。 BOOL ReleaseMutex( HANDLE hMutex //欲释放mutex的handle ); 返回值 如果成功,则返回true,否则返回false。 3.信号量(Semaphores) Mutex 是semaphore的一种退化,如果你产生一个semaphore并令最大值为1,那它就是个mutex。因此,mutex又常被称为binary semaphore。在许多系统中,semaphore常被使用,因为mutex可能并不存在,在win32中semaphore被使用的情况就少得多, 因为mutex存在的原因。 一旦semaphore的现值降到0,就表示资源已经耗尽。此时任何线程如果调用Wait…函数,必然要等待,直到某个锁定被解除。 HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName ) 参数: lpSemaphoreAttributes:安全属性,null表示使用默认属性。 lInitialCount:初始值,必须>=0,并且<= lMaximumCount lMaximumCount:最大值,也就是在同一时间内能够锁住semaphore的线程数 lpName:名称,这个值也可以是null。 返回值: 如果成功就返回一个handle,否则返回null。如果semaphore名称已经存在,函数还是会成功,GetLastError会返回ERROR_ALREADY_EXISTS 函数说明: 产生一个semaphore。 BOOL ReleaseSemaphore( HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount ); 参数: hSemaphore:semaphore的句柄。 lReleaseCount:semaphore现值的增量,通常是1,该值不可以是负值或者0 lpPreviousCount:返回semaphore增加前的现值 返回值: 如果成功就返回true,否则返回false。 函数说明: 三、事件(Events) 事件(Event)是一种核心对象,它的唯一目的就是成为激发状态或未激发状态。这两种状态完全在你的掌握之下,不会因为Wait…函数的调用而变化。 HANDLE CreateEvent( LPCTSTR lpName ); 参数: lpEventAttributes:安全属性,null表示使用默认属性。 bManualReset : 此值为false,表示event变成激发状态后,自动重置为非激发状态; 此值为true,表示不会自动重置,必须靠程序(调用ResetEvent)操作才能将激发状态的event重置为非激发状态。 bInitialState :初始状态,true一开始处于激发状态,false一开始处于非激发状态 lpName :Event对象名 返回值: 如果成功就返回一个handle,否则返回null。如果event名称已经存在,函数还是会成功,GetLastError会返回ERROR_ALREADY_EXISTS BOOL SetEvent(HANDLE hEvent); //把event对象设为激发状态 BOOL ResetEvent(HANDLE hEvent); //把event对象设为非激发状态 BOOL PulseEvent(HANDLE hEvent ); //如果event的bManualReset 为true:把event对象设为激发状态,唤醒所有等待中的线程,然后event恢复为非激发状态 //如果event的bManualReset 为false:把event对象设为激发状态,唤醒一个等待中的线程,然后event恢复为非激发状态 5.使用Synchronize方法 这个方法用于访问VCL主线程所管理的资源,其方法的应用是: 6、使用VCL类的Look方法 在
Delphi的IDE提供的构件中,有一些对象内部提供了线程的同步机制,工作线程可以直接使用这些控件,比
如:Tfont,Tpen,TBitmap,TMetafile,Ticon等。另外,一个很重要的控件对象叫TCanvas,提供了一个Lock方法用
于线程的同步,当一个线程使用此控件对象的时候,首先调用这个对象的Lock方法,然后对这个控件进行操作,完毕后再调用Unlock方法,释放对控间的
控制权。 四、线程的优先级: 在多线程的情况下,一般要根据线程执行任务的重要性,给线程适当的优先级,一般如果量的线程同时申请CPU时间,优先级高的线程优先。 优先权类别(Priority Class) Win32提供四种优先权类别,每一个类别对应一个基本的优先权层次。 表格5-1优先权类别
大部分程序使用NORMAL_PRIORITY_CLASS。优先权类别是进程的属性之一,利用SetPriorityClass和GetPriorityClass函数可以调整和获取该值。 优先权层次(priority Level) 线程的优先权层次使你能够调整同一个进程内的各线程的相对重要性。一共七种优先权层次: 表格5-2
利用SetThreadPriority和GetThreadPriority函数可以调整和获取该值 在Windows下,给线程的优先级分为30级,而Delphi中Tthread对象相对简单的把优先级分为七级。也就是在Tthread中声明了一个枚举类型TTthreadPriority: BOOL SetThreadPriority( HANDLE hThread, //欲调整优先权的那个线程的句柄 int nPriority //表格5-2所显示的值 ); Int GetThreadPriority( HANDLE hThread //线程的句柄 ); 返回值是线程的优先级。 |
|
来自: 昵称13729677 > 《delphi》