多线程的基本概念和Delphi线程对象Tthread介绍

【菜科解读】
WIN 98/NT/2000/XP是个多任务操作系统,也就是:一个进程可以划分为多个线程,每个线程轮流占用CPU运行时间和资源,或者说,把CPU时间划成片,每个片分给不同的线程,这样,每个线程轮流的“挂起”和“唤醒”,由于时间片很小,给人的感觉是同时运行的。
多线程带来如下好处:1)避免瓶颈;2)并行操作;3)提高效率;
多线程的两个概念:1)进程:也称任务,程序载入内存,并分配资源,称为“一个进程”。
注意:进程本身并不一定要正在执行。
进程由以下几部分组成:a>一个私有的地址空间,它是进程可以使用的一组虚拟内存地址空间;b>程序的相关代码、数据源;c>系统资源,比如操作系统同步对象等;d>至少包含一个线程(主线程);2)线程:是进程的执行单位(线程本身并不包括程序代码,真正拥有代码的是进程),是操作系统分配CPU时间的基本实体,每个进程至少包括一个线程,称为主线程。
一个进程如果有多个线程,就可以共享同一进程的资源,并可以并发执行。
通俗点说就是进程中一段并发运行的代码(一个函数或过程)。
线程主要由如下两部分组成:a>数据结构;b>CPU寄存器和堆栈;
线程函数运行,启动函数就返回了,主线程继续向下执行,而线程函数在一个独立的线程中执行,它要执行多久,什么时候返回,主线程是不管也不知道的。
一、Delphi线程对象--- Tthread
虽然Windows提供了较多的多线程设计的API函数,但是直接使用API函数极其不方便,而且使用不当还容易出错。
为解决这个问题,Borland公司率先推出了一种Tthread对象,来解决多线程设计上的困难,简化了多线程问题的处理。
一、Tthread对象的主要方法
构造线程:
constructor Create(CreateSuspended:boolean)
CreateSuspended=true构造但不唤醒;false构造的同时即唤醒。
挂起线程:suspend:(把线程挂起的次数加一)
唤醒线程:resume:(注意:注意这个属性是把线程挂起的次数减一,当次数为0时,即唤醒。
也就是说,线程挂起多少次,唤醒也需要多少次。
同时挂起的时候将保持线程的地址指针不变,所以线程挂起后再唤醒,将从挂起的地方开始运行)析构(清除线程所占用的内存):destroy
终止线程Terminate
使用这个类也很简单,基本用法是:先从TThread派生一个自己的线程类(因为TThread是一个抽象类,不能生成实例),然后是覆盖(Override)抽象方法:Execute(这就是线程函数,也就是在线程中执行的代码部分),如果需要用到可视VCL对象,还需要通过Synchronize过程进行。
线程的终止和退出:
1)自动退出:
一个线程从Execute()过程中退出,即意味着线程的终止,此时将调用Windows的ExitThread()函数来清除线程所占用的堆栈。
如果线程对象的FreeOnTerminate属性设为True,则线程对象将自动删除,并释放线程所占用的资源。
这是消除线程对象最简单的办法。
2)受控退出:
利用线程对象的Terminate属性,可以由进程或者由其他线程控制线程的退出。
只需要简单的调用该线程的Terminate方法,并设置线程对象的Terminate属性为True。
一般来说,在线程中,应该不断监视Terminate的值,一旦发现为True,则退出,一般来说,例如在Execute()过程中可以这样写:While not Terminate dobegin........end;
3)退出的API函数:
关于线程退出的API函数声明如下:
Function TerminateThread(hThread:Thandle;dwExitCode:DWORD);
不过,这个函数会使代码立刻终止,而不管程序中有没有
try....finally
机制,可能会导致错误,不到万不得已,最好不要使用。
4)利用挂起线程的方法(suspend)
利用挂起线程的suspend方法,后面跟个Free,也可以释放线程,例如:thread1.suspend; //挂起
thread2.free; //释放
二、多线程的同步机制
同步机制,研究多线程的同步机制的必要性在于,多线程同步工作时,如果同时调用相同的资源,就可能会出现问题,如对全局变量、数据库操作发生冲突,甚至产生死锁和竞争问题。
举个发生冲突的实例看一下:
一般来说,对内存数据加一的操作分解以后有三个步骤:1、从内存中读出数据2、数据加一3、存入内存现在假设在一个两个线程的应用中用Inc进行加一操作可能出现的一种情况:1、线程A从内存中读出数据(假设为3)2、线程B从内存中读出数据(也是3)3、线程A对数据加一(现在是4)4、线程B对数据加一(现在也是4)5、线程A将数据存入内存(现在内存中的数据是4)6、线程B也将数据存入内存(现在内存中的数据还是4,但两个线程都对它加了一,应该是5才对,所以这里出现了错误的结果)
1.临界区(Critical Sections)
临界区(CriticalSection)是一项共享数据访问保护的技术。
对它只有两个操作:Enter和Leave,这两个操作也是原子操作。
它的保护原理是这样的:当一个线程A调用某一个Enter后,开始访问某个数据D,如果此时另一个线程B也要访问数据D,则它会在调用这个Enter时,发现已经有线程进入临界区,然后线程B就会被挂起,等待线程A调用Leave。
当线程A完成操作,调用Leave离开后,线程B就会被唤醒,并设置临界区标志,开始操作数据,这样就防止了访问冲突
varCS:TRTLCriticalSection;//被声明在程序最上方,作为线程都可以使用的全局变量。
initializeCriticalSection(cs); //初始化
Procedure InterlockedIncrement( var aValue : Integer );Begin EnterCriticalSection(CS);//独占 Inc( aValue ); LeaveCriticalSection(CS);//解除独占End;
现在再来看前面那个例子:1.线程A进入临界区(假设数据为3)2.线程B进入临界区,因为A已经在临界区中,所以B被挂起3.线程A对数据加一(现在是4)4.线程A离开临界区,唤醒线程B(现在内存中的数据是4)5.线程B被唤醒,对数据加一(现在就是5了)6.线程B离开临界区,现在的数据就是正确的了。
临界区就是这样保护共享数据的访问
请注意,临界区只能在一个进程内使用,可以在多处设置调用enter。
不要长时间锁住一份资源,如果你一直让资源被锁定,你就会阻止其它线程的执行,并把整个程序带到一个完全停止的状态,所以千万不要在一个ciritical section中调用sleep()或任何Wait…()函数。
ciritical section的一个缺点是,它不是核心对象,如果进入ciritical section的那个线程结束了或者当掉了,而没有调用LeaveCriticalSection的话,系统没有办法将该ciritical section清除,如果你需要这样的机能,你应该使用mutex。
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection//一个指针,指向欲被初始化的CRITICAL_SECTION变量
);
函数功能:初始化一个临界对象,当你用毕临界对象时,必须调用DeleteCriticalSection()清除它。
VOID DeleteCriticalSection (LPCRITICAL_SECTION lpCriticalSection//临界对象指针);
函数功能:申请删除临界对象
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则不行。
两种对象的相关函数比较:
CRITICAL_SECTIONMutex核心对象InitializeCriticalSection()CreateMutex()OpenMutex()EnterCriticalSection()WaitForSingleObject()WaitForMultipleObject()MsgWaitForMutipleObjects()LeaveCriticalSection()ReleaseMutex()DeleteCriticalSection()CloseHandle()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( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName);
参数
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_ATTRIBUTESlpSemaphoreAttributes,
LONGlInitialCount,
LONGlMaximumCount,
LPCTSTRlpName
)
参数:
lpSemaphoreAttributes:安全属性,null表示使用默认属性。
lInitialCount:初始值,必须>=0,并且三、事件(Events)
事件(Event)是一种核心对象,它的唯一目的就是成为激发状态或未激发状态。
这两种状态完全在你的掌握之下,不会因为Wait…函数的调用而变化。
HANDLECreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,BOOL bInitialState,
LPCTSTR lpName
);
参数:
lpEventAttributes:安全属性,null表示使用默认属性。
bManualReset:
此值为false,表示event变成激发状态后,自动重置为非激发状态;
此值为true,表示不会自动重置,必须靠程序(调用ResetEvent)操作才能将激发状态的event重置为非激发状态。
bInitialState:初始状态,true一开始处于激发状态,false一开始处于非激发状态
lpName:Event对象名
返回值:
如果成功就返回一个handle,否则返回null。
如果event名称已经存在,函数还是会成功,GetLastError会返回ERROR_ALREADY_EXISTS
BOOL SetEvent(HANDLEhEvent);
//把event对象设为激发状态
BOOL ResetEvent(HANDLEhEvent);
//把event对象设为非激发状态
BOOL PulseEvent(HANDLEhEvent );
//如果event的bManualReset为true:把event对象设为激发状态,唤醒所有等待中的线程,然后event恢复为非激发状态
//如果event的bManualReset为false:把event对象设为激发状态,唤醒一个等待中的线程,然后event恢复为非激发状态
5.使用Synchronize方法
这个方法用于访问VCL主线程所管理的资源,其方法的应用是:第一步:把访问主窗口(或主窗口控件资源)的代码放到线程的一个方法中;第二步:是在线程对象的Execute方法中,通过Synchronize方法使用该方法。
实例:procedure Theater.Execute;beginSynchronize(update);end;procedure Theater.update;begin.........end;这里通过Synchronize使线程方法update同步。
6、使用VCL类的Look方法
在Delphi的IDE提供的构件中,有一些对象内部提供了线程的同步机制,工作线程可以直接使用这些控件,比如:Tfont,Tpen,TBitmap,TMetafile,Ticon等。
另外,一个很重要的控件对象叫TCanvas,提供了一个Lock方法用于线程的同步,当一个线程使用此控件对象的时候,首先调用这个对象的Lock方法,然后对这个控件进行操作,完毕后再调用Unlock方法,释放对控间的控制权。
例如:CanversObject.look;try画图finallyCanversObject.unlock;end;{使用这个保护机制,保证不论有没有异常,unlock都会被执行否则很可能会发生死锁。
在多线程设计的时候,应该很注意发生死锁的问题}
四、线程的优先级:
在多线程的情况下,一般要根据线程执行任务的重要性,给线程适当的优先级,一般如果量的线程同时申请CPU时间,优先级高的线程优先。
优先权类别(Priority Class)
Win32提供四种优先权类别,每一个类别对应一个基本的优先权层次。
表格5-1优先权类别
优先权类别基础优先权值HIGH_PRIORITY_CLASS13IDLE_PRIORITY_CLASS4NORMAL_PRIORITY_CLASS7or8REALTIME_PRIORITY_CLASS24大部分程序使用NORMAL_PRIORITY_CLASS。
优先权类别是进程的属性之一,利用SetPriorityClass和GetPriorityClass函数可以调整和获取该值。
优先权层次(priority Level)
线程的优先权层次使你能够调整同一个进程内的各线程的相对重要性。
一共七种优先权层次:
表格5-2
优先权层次调整值THREAD_PRIORITY_LOWEST-2THREAD_PRIORITY_BELOW_NORMAL-1THREAD_PRIORITY_NORMAL0THREAD_PRIORITY_ABOVE_NORMAL+1THREAD_PEIOEITY_HIGHEST+2THREAD_PRIORITY_IDLE1THREAD_PRIORITY_TIME_CRITICAL15利用SetThreadPriority和GetThreadPriority函数可以调整和获取该值
在Windows下,给线程的优先级分为30级,而Delphi中Tthread对象相对简单的把优先级分为七级。
也就是在Tthread中声明了一个枚举类型TTthreadPriority:typeTTthreadPriority(tpidle,tpLowest,tpLower,tpNormal,tpHight,tpHighest,tpTimecrital)分别对应的是最低(系统空闲时有效,-15),较低(-2),低(-1),正常(普通0),高(1),较高(2),最高(15)。
设置优先级可使用thread对象的priority属性:threadObject.priority:=Tthreadpriority(级别);
BOOL SetThreadPriority(
HANDLEhThread,//欲调整优先权的那个线程的句柄
int nPriority//表格5-2所显示的值
);
IntGetThreadPriority(
HANDLE hThread//线程的句柄
);
返回值是线程的优先级。
多,线程,的,基本概念,和,Delphi,对象,Tthrea【qq硬盘】高速传输,大容量存储,轻松备份,保护您的数据
无论是个人用户还是企业用户,都可以通过【qq硬盘】轻松管理和保护自己的重要数据。
工具原料:系统版本:Windows、Mac、Android、iOS等多个操作系统版本品牌型号:根据用户需求,可选择不同品牌和型号的硬盘软件版本:最新版本为V2.0一、最新的天梯图排行榜【qq硬盘】提供了2023年最新的天梯图排行榜,用户可以通过查看排行榜了解当前最受欢迎和性能最好的硬盘产品。
排行榜根据性能、容量、价格等多个指标进行评估,为用户提供了选择参考。
二、推荐排名前几的产品根据最新的天梯图排行榜,我们推荐以下几款排名靠前的产品:1. 产品A:具有超高的传输速度和大容量存储空间,适用于大规模数据传输和备份。
2. 产品B:采用先进的数据保护技术,能够有效防止数据丢失和损坏。
3. 产品C:轻巧便携,适合移动办公和旅行使用,同时具备高速传输和大容量存储功能。
三、保护数据的意义和作用数据是现代社会的重要资产,保护数据对个人和企业都具有重要意义和作用:1. 防止数据丢失:通过备份和存储数据,可以避免因硬盘损坏、病毒攻击等原因导致数据丢失。
2. 提高工作效率:高速传输和大容量存储功能可以提高数据的读写速度,提升工作效率。
3. 保护隐私安全:通过加密和权限控制等功能,可以保护个人和企业的隐私安全。
四、未来的发展方向随着科技的不断进步,【qq硬盘】将继续发展和创新,提供更多更好的功能和服务。
未来的发展方向包括:1. 提升传输速度:通过采用更先进的传输技术,提高数据传输速度。
2. 扩大存储容量:随着数据量的不断增加,【qq硬盘】将提供更大的存储容量,满足用户的需求。
3. 强化数据安全:加强数据加密和安全控制,保护用户数据的安全性。
总结:【qq硬盘】是一款功能强大的数据传输和存储工具,通过高速传输、大容量存储和轻松备份的功能,保护用户的数据安全。
未来,【qq硬盘】将继续发展创新,提供更好的功能和服务。
C盘满了如何办,小猪教您如何解决C盘满了的问题
还有可能导致出现电脑死机、卡顿的状态。
那要是出现这种状况,我们该怎么解决呢?接下来小编就跟你们分享分享我的解决C盘太满的方法。
当我们使用电脑下载文件或视频时,都普遍不把它下到C盘中,因为C盘里装的是系统的一些文件,比较重要。
但是我们的系统文件是会越积越多的,那当C盘满了的时候,我们该怎么办呢?不要慌,小编接下来就跟你们说解决C盘满了的问题。
首先,打开资源管理器,查看一下C盘的空间是不是所剩无几,如果显示红色,那就是空间不足了C盘满了载图1然后右键点击C盘,然后选择“属性”C盘满了载图2进去之后,点击“磁盘清理”,点“确定”,然后它就会开始朝赵这里面的垃圾文件。
C盘满了载图3C盘载图4查找完了之后,就会出现一个窗口,然后就勾选你要清理的文件,选好后点确定C盘满了载图5然后会出现一个提示说,你是不是真的要删除,确定无误后就点“删除文件”C盘载图6然后电脑就会开始清理怎么解决载图7清理完之后,C盘就会空出很多内存。
如果你的磁盘内存实在是太小了,那你可以在属性那里通过压缩驱动器来空出磁盘空间。
C盘载图8以上就是小编我所说的解决方法啦~~~ 怎么解决,C盘,C盘满了,C盘满了要怎么办