注册通行证 用户名 密码
  • 文章投稿
  • 博客
  • 论坛
  • 设为首页
  • 加入收藏
jztop.com网络技术
  • 首页
  • | iT新闻
  • | 操作系统
  • | 组网建网
  • | 网络安全
  • | 程序开发
  • | 办公一族
  • | 工具软件
  • | 网页制作
  • | 多媒体制作
  • | 网吧技术
  • | 服务器
  • | 专题教程
Vista | 软件评测 | 系统备份 | 优化 | 进程 | 聊天 | 病毒 | Linux | 黑客 | 防火墙 | 数据库 | Web开发 | Java | Word | 游戏 | 32位开发 | 移动开发
当前位置:首页 > 程序开发 > 32位开发 > C/C++ > 内容正文

漫谈C++ Builder多线程编程技术

发布时间:2006-05-05 19:02:23 来源:计算机与信息技术 网友评论 0 条

  摘 要:本文简单介绍了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平台下如何利用多线程编程技术实现"生产者-消费者"问题,帮助我们更好得理解同步概念及其实现方法。

  生产者-消费者问题

  生产者-消费者问题是一个著名的进程同步问题。它描述的是:有一群生产者进程在生产消息,并将此消息提供给消费者进程去消费。为使生产者进程和消费者进程能并发进行,在他们之间设置了一个具有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、协调线程

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

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

   消费者线程:

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

关于 漫谈C+ 的新闻
    无相关信息
【评论】【收藏本文】【打印】【关闭】
上一篇文章:用C++ Builder实现电子邮件群发
下一篇文章:VC++下借助DBGird控件显示数据库记录
讨论区
查看
已有 0 位对此新闻感兴趣的网友发表了看法
匿名发表
注册通行证 登陆
图文阅读推荐
全站资源
  • 微软官方入门教程19:轻松掌握Vista系统的快
  • 微软2008大冲击,预借Vista SP1力促Vista市
  • 在收件箱中获得 Windows Vista 的最新更新
  • 微软官方Vista入门教程全集19篇(Vista学院
  • Windows Vista 的成功将势不可挡
  • 快快抛弃Vista,拥抱XP SP3!你觉得呢?
  • 浅谈Vista系统关闭虚拟内存与使用内存盘加速
  • 嘿嘿,按下键盘上面的三个键,马上让你的Vi
  • Windows Vista的盗版率只有Windows XP的一半
  • 3DMark和PCMark Vantage新版将只支持Vista系
阅读排行
  • Borland 发布C++ Builder 2006 RAD 环境
  • C/C++程序员应聘常见面试题深入剖析
  • Visual C++常用数据类型转换详解
  • C++中的 static 关键字
  • 利用VC++实现局域网实时视频传输
  • 浅谈C/C++内存泄漏及其检测工具
  • 英国投票否决C++/CLI 微软强攻ISO标准受挫
  • VC++下用MSComm控件实现串口通讯
  • 伪随机数生成及在VC++中的实现
  • VC++编程实现对波形数据的频谱分析
最新技术文档
  • C++中用赋值形式op=取代单独形式op
  • C++启蒙之用C++制作程序
  • C++启蒙之C++中的类
  • 解析C++/CLI之头文件、内联函数与数组
  • C++启蒙之控制结构
  • C++启蒙之注释符号
  • 了解C++异常处理的系统开支
  • C++语言学习之从结构到类
  • C++中理解“传递参数”和异常之间的差异
  • C++中禁止异常信息传递到析构函数外
专题教程
  • 大话G游 专题:手机病毒揭密
  • ARP攻击防范与解决方案 路由故障处理手册
  • Picasa中文版_Picasa教程 专题:清除流氓软件
  • Firefox专题 seo搜索引擎优化专区
  • 重装Windows必知的事情 装机之必备软件大行动
病毒专杀栏
  • 杀毒软件反被病毒杀 连"救命"都不能喊
  • 金山ARP防火墙
  • 还原卡神话破灭“机器狗”病毒来势汹汹
  • cctv经济半小时:你的手机现在安全吗?
  • 新挂马方式开始流行 ARP挂马称雄局域网
  • 木马和病毒清除的通用解法
  • IP地址不再冲突 查找ARP攻击者元凶
  • 教你几招识别和防御Web网页木马
  • 分析:封杀BT只是暂时的止痛药
  • QQ爆危险漏洞,“QQ游戏邀请大盗”邀请你玩病
关于我们 | 诚聘英才 | 联系我们 | 版权声明 | 网站大事 | 网站地图 | 意见建议
CopyRight 2005-2007 Jztop.Com 版权所有 未经许可 请勿转载