C++ Builder多线程编程技术经验谈(转)

源:C++ Builder多线程编程技术经验谈html

 

线程之可行性编程

   在不少状况下,可能须要为程序建立线程。这里给出其中一些可能性:     安全

  (1)若是建立的是一个多文档接口(Multiple Document Interface,MDI)程序,那么为每一个窗口分配一个线程就显得十分重要了,例如,对于一个经过多个Modem同时链接到多个主机的MDI通讯程序而言,若是每一个窗口都有它本身的线程来和一个主机通讯,那么整个事情就简化不少。     多线程

  (2)若是使用的是一台有多个处理器的机器,并但愿充分利用全部可能得到的CPU资源,那么就须要将应用程序分解成多个线程。Windows2000中CPU的划分单位为线程。所以,若是程序只包含一个线程,那么,默认环境下该程序只能使用其中一个CPU.可是,若是将此程序划分为多个线程,那么Windows2000就能够在不一样的CPU上运行各个线程。     并发

  (3)在后台运行的某些任务的同时,要求用户还能够继续使用应用程序进行工做。利用线程很容易实现这点。例如:能够将一些冗长的重算、页面格式化操做、文件的读写等活动都放在单独的线程中,使其在后台运行,而不会对用户形成影响。     异步

 

同步     函数

  撰写多线程程序的一个最具挑战性的问题就是:如何让一个线程和另外一个线程合做。这引出了一个很是重要的问题:同步。所谓同步是指进程、线程间相互通讯时避免破坏各自数据的能力。Windows环境下的同步问题是由Win32系统的CPU时间片分配方式引发的。虽然在某一时刻,只有一个线程占用CPU(单CPU)时间,可是没法知道在何时,在什么地方线程被打断,这样如何保证线程之间不破坏彼此的数据就显得格外重要。同步问题是如此重要,也至关有趣,于是吸引了很多学者对他进行研究,由此产成了一系列经典的进程同步问题,其中较有表明性的是"生产者-消费者问题"、"读者-写者问题""哲学家进餐问题"等。在此,本文简要讨论了C++Builder平台下如何利用多线程编程技术实现"生产者-消费者"问题,帮助咱们更好得理解同步概念及其实现方法。     ui

 

生产者-消费者问题     spa

  生产者-消费者问题是一个著名的进程同步问题。它描述的是:有一群生产者进程在生产消息,并将此消息提供给消费者进程去消费。为使生产者进程和消费者进程能并发进行,在他们之间设置了一个具备N个缓冲区的缓冲池,生产者进程能够将它所生产的消息放入一个缓冲区中,消费者进程能够从一个缓冲区中取得一个消息消费。尽管全部的生产者进程和消费者进程都是以异步方式进行的,但他们之间必须保持同步,即不容许消费者进程到一个空的缓冲区中去取消息,也不容许生产者进程向一个已装满消息且还没有被取走消息的缓冲区中投放消息。     线程

 

C++Builder多线程应用程序编程基础     

一、使用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属性的数值。 

 

二、协调线程     

  在编写线程执行时运行的代码时,必须考虑到可能同步执行的其余线程的行为。特别注意,避免两个线程试图同时使用相同的全局对象或变量。另外,一个线程中的代码会依赖其余线程执行任务的结果。     

  (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实现了线程之间地同步。 

  

3 等待其余线程     

  若线程必须等待另外一线程完成某项任务,可以让线程临时中断执行。而后,要么等待另外一线程彻底执行结束,要么等待另外一线程通知完成了该任务。     

  (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 ();
}
}

 

消费者线程

 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环境下进行多线程编程技术及其实现的做用和效果。

相关文章
相关标签/搜索