漫谈C++ Builder多线程编程技能
副标题#e#
摘 要:本文简朴先容了Windows情况下举办多线程编程的意义,重点接头了C++Builder情况下开拓多线程应用措施这一问题,并通过实现出产者-消费者问题,帮我们更好地领略同步观念及其实现要领。
要害词:多线程;同步;出产者-消费者;C++Builder
线程之可行性
在许多环境下,大概需要为措施建设线程。这里给出个中一些大概性:
(1)假如建设的是一个多文档接口(Multiple Document Interface,MDI)措施,那么为每个窗口分派一个线程就显得十分重要了,譬喻,对付一个通过多个Modem同时毗连到多个主机的MDI通信措施而言,假如每个窗口都有它本身的线程来和一个主机通信,那么整个工作就简化许多。
(2)假如利用的是一台有多个处理惩罚器的呆板,并但愿充实操作所有大概得到的CPU资源,那么就需要将应用措施解析成多个线程。Windows2000中CPU的分别单元为线程。因此,假如措施只包括一个线程,那么,默认情况下该措施只能利用个中一个CPU。可是,假如将此措施分别为多个线程,那么Windows2000就可以在差异的CPU上运行各个线程。
(3)在靠山运行的某些任务的同时,要求用户还可以继承利用应用措施举办事情。操作线程很容易实现这点。譬喻:可以将一些冗长的重算、页面名目化操纵、文件的读写等勾当都放在单独的线程中,使其在靠山运行,而不会对用户造成影响。
同步
撰写多线程措施的一个最具挑战性的问题就是:如何让一个线程和另一个线程相助。这引出了一个很是重要的问题:同步。所谓同步是指历程、线程间彼此通信时制止粉碎各自数据的本领。Windows情况下的同步问题是由Win32系统的CPU时间片分派方法引起的。固然在某一时刻,只有一个线程占用CPU(单CPU)时间,可是无法知道在什么时候,在什么处所线程被打断,这样如何担保线程之间不粉碎互相的数据就显得分外重要。同步问题是如此重要,也相当有趣,因而吸引了不少学者对他举办研究,由此产成了一系列经典的历程同步问题,个中较有代表性的是"出产者-消费者问题"、"读者-写者问题""哲学家进餐问题"等。在此,本文扼要接头了C++Builder平台下如何操作多线程编程技能实现"出产者-消费者"问题,辅佐我们更好得领略同步观念及其实现要领。
#p#副标题#e#
出产者-消费者问题
出产者-消费者问题是一个著名的历程同步问题。它描写的是:有一群出产者历程在出产动静,并将此动静提供应消费者历程去消费。为使出产者历程和消费者历程能并发举办,在他们之间配置了一个具有N个缓冲区的缓冲池,出产者历程可以将它所出产的动静放入一个缓冲区中,消费者历程可以从一个缓冲区中取得一个动静消费。尽量所有的出产者历程和消费者历程都是以异步方法举办的,但他们之间必需保持同步,即不答允消费者历程到一个空的缓冲区中去打动静,也不答允出产者历程向一个已装满动静且尚未被取走动静的缓冲区中投放动静。
C++Builder多线程应用措施编程基本
1、利用C++Builder提供的TThread类
VCL类库提供了用于线程编程的TThread类。在TThread类中封装了Windows中关于线程机制的WindowsAPI。对付大大都的应用措施来说,可在应用措施中利用线程工具来暗示执行线程。线程工具通过封装利用线程所需的内容,简化了多线程应用措施的编写。留意,线程工具不答允节制线程仓库的巨细或其安详属性。若需要节制这些,必需利用WindowsAPI的Create Thread()或Begin Thread()函数。
TThread类有以下一些属性和要领:
1) 属性:
·Priority:优先级属性。可以配置线程的优先级。
·Return Value:返回值属性。当线程先容时返回给其他线程一个数值。
·Suspended:挂起属性。可以判定线程是否被挂起。
·Terminated:竣事属性。用来符号是否应该竣事线程。
·ThreadID:标识号属性。在整个系统中线程的标识号。利用Windows API函数时该属性很是有用。
2) 要领:
·Do Terminate:发生一个On Terminate事件,可是不竣事线程的执行。
·Resume:叫醒一个线程继承执行。
·Suspend:挂起一个线程,要与Resume进程成对利用。
·Synchronize:由主VCL线程挪用的一个同步进程。
·Terminate:将Terminate属性配置为True,中止线程的执行。
·Wait For:期待线程的中止并返回Return Value属性的数值。
2、协调线程
#p#分页标题#e#
在编写线程执行时运行的代码时,必需思量到大概同步执行的其他线程的行为。出格留意,制止两个线程试图同时利用沟通的全局工具或变量。别的,一个线程中的代码会依赖其他线程执行任务的功效。
1) 制止同时会见
为制止在会见全局工具或变量时与其他线程产生斗嘴,大概需要暂停其他线程的执行,直到该线程代码完成操纵。
(1)锁定工具。一些工具内置了锁定成果,以防备其他线程利用该工具的实例。譬喻,画布工具(TCanvas及其派生类)有一种Lock()函数可以防备其他线程会见画布,直到挪用Unlock()函数。显然,这种要领只对部门类有效。
(2)利用重要区段。若工具没有提供内置的锁定成果,可利用重要区段。重要区段像门一样,每次只答允一个线程进入,要利用重要区段,需建设TCriticalSection的全局实例。TCriticalSection有两个函数:Acquire()(阻止其他线程执行该区域)及Release()(打消对其他线程的阻止)。
(3)利用多重读、独有写的同步器。当利用重要区段来掩护全局内存时,每次只有一个线程可以利用该内存。这种掩护大概会超出了需要,出格是有一个常常读但很少写的工具或变量时更是如此。多个线程同时读沟通内存但没有线程写内存是没有危险的。当有一些常常被读,可是很少写的全局变量时,可用TMultiReadExclusiveWriteSynchronizer工具掩护它。这个工具和重要区段一样,但它答允多个线程同时读,只要没有线程写即可。每个需要读内存的线程首先要挪用Begin Read()函数(确保当前无其他线程写内存),线程完成对掩护内存读操纵后,要挪用End Read()函数。任何线程需要写掩护内存必需挪用Begin Write()函数(确保当前无其他线程读或写内存),完成对掩护内存写操纵后,挪用End Write()函数。
(4)利用Synchronize函数:Void __fast call Synchronize (TThreadMethod &Method);
个中参数Method为一个不带参数的进程名。在这个不带参数的进程中是一些会见VCL的代码。我们可以在Execute进程中挪用Synchronize进程来制止对VCL的并发会见。措施运行期间的详细进程实际上是由Synchronize进程来通知主线程,然后主线程在适当的机缘来执行Synchronize进程的参数列表中的谁人不带参数的进程。在多个线程的环境下,主线程将Synchronize进程发过来的通知放到动静行列中,然后逐个地响应这些动静。通过这种机制Synchronize实现了线程之间地同步。
2) 期待其他线程
若线程必需期待另一线程完成某项任务,可让线程姑且间断执行。然后,要么期待另一线程完全执行竣事,要么期待另一线程通知完成了该任务。
(1)期待线程执行竣事
要期待另一线程执行竣事,利用它地Wait For()函数。Wait For函数直到谁人线程终止才返回,终止的方法要么完成了其Execute()函数,要么由于一个异常。
(2)期待任务完成。有时,只需要期待线程完成一些操纵而不是期待线程执行竣事。为此,可利用一个事件工具。事件工具(TEvent)应具有全局范畴以便他们可以或许为所有线程可见。当一个线程完成一个被其他线程依赖的操纵时,挪用TEvent::Set Event()函数。Set Event发出一个信号,以便其他线程可以查抄并得知操纵完成。要关掉信号,则利用Reset Event()函数。
譬喻,当必需期待若干线程完成其执行而不是单个线程时。因为不知道哪个线程最后完成,也就不能对某个线程利用Wait For()函数。此时,可通过挪用Set Event以在线程竣事时累加计数值并在最后一个线程竣事时发出信号以指示所有线程竣事。
多线程应用措施编程实例
下面是一个实现"出产者-消费者问题"的多线程应用实例。在此例中,我们按上面先容的要领结构了两个TThread的子类TProducerThread(出产者线程)和TCustomerThread(消费者线程),出产和消费的商品仅仅是一个整数。在协调出产和消费的进程中,重要区段(TCriticalSection)和事件(TEvent)获得了应用。出产者通过TEvent类的工具Begin Consume来通知消费者开始消费,而消费者通过TEent类的工具Begin Produce通知出产者开始出产。措施中共有两个出产者,一个消费者。在两个出产者之间,通过TCriticalSection类的工具同步。其运行界面如图1所示。
图1 措施运行结果
主要源措施如下所示:
出产者线程:
Void __fast call TProducerThread:: Execute ()
{
//---- Place thread code here ----
Int i = 0;
Int j;
while(i<100) //每个出产者线程出产100个商品
{
Sleep(1000);//延迟,为清楚得显示执行结果
if(Form1->buffer_size > 0)//缓冲池不空,通知消费者消费
{
Form1->Begin Consumer->Set Event ();
}
Form1->Produce Guard->Acquire ();
i++;
StrResult = IntToStr (i);
J = Form1->buffer_size;
Form1->Product [j] = i;
Form1->buffer_size++;
Synchronize(Show Result);//刷新界面,显示最新出产-消费状况
Form1->Begin Consumer->Set Event();//通知消费者消费
if(Form1->buffer_size == 5)//缓冲池满,挂起出产者线程,直到通知再出产
{
Form1->Begin Produce->Wait For (INFINITE);
}
Sleep (1000);
Form1->Produce Guard->Release ();
}
While (Form1->buffer_size > 0)
{
Form1->Begin Consumer->Set Event ();
}
}
#p#分页标题#e#
消费者线程:
Void __fast call TConsumerThread::Execute()
{
//---- Place thread code here ----
Int j;
For (int i = 0;i < 200;i++)
{
Sleep(100); //延迟,为清楚得显示执行结果
Form1->Begin Consumer->Wait For(INFINITE);//挂起消费者线程,直到通知再消费
J = Form1->buffer_size - 1;
StrResult = IntToStr (Form1->Product [j]);
Form1->buffer_size--;
Synchronize(Show Result); //刷新界面,显示最新出产-消费状况
if(Form1->buffer_size == 4)//缓冲池不再full,叫醒由于缓冲池full而挂起的出产者线程
{
Form1->Begin Produce->Set Event ();
}
Sleep (100);
}
}
结论
本文接头了多线程编程及其可行性,说明白在Windows情况下举办多线程编程的意义,并重点接头了C++Builder平台下如何开拓多线程应用措施这一问题,通过实现"出产者-消费者问题"这一著名的历程同步问题,较量清晰地反应了在Windows情况下举办多线程编程技能及其实现的浸染和结果。