多线程下的消息处理
- 格式:pdf
- 大小:121.86 KB
- 文档页数:2
java多线程实际应用案例Java多线程是一种并发编程的方式,可以使程序同时执行多个任务,提高程序的执行效率和响应速度。
下面列举了十个Java多线程实际应用案例。
1. 电商网站订单处理:在一个电商网站中,订单的处理是一个非常繁琐且耗时的工作,可以使用多线程实现订单的并发处理,提高订单处理的效率。
2. 聊天软件消息发送:在聊天软件中,用户发送消息是一个频繁的操作,可以使用多线程实现消息的并发发送,提高用户体验。
3. 数据库读写操作:在数据库的读写操作中,读操作可以使用多线程并发执行,提高数据的读取速度;写操作可以使用多线程并发执行,提高数据的写入速度。
4. 图像处理:在图像处理中,可以使用多线程实现图像的并行处理,提高图像处理的速度。
5. 视频编解码:在视频编解码中,可以使用多线程实现视频的并行编解码,提高视频的处理速度。
6. 网络爬虫:在网络爬虫中,可以使用多线程实现并发的爬取网页数据,提高爬虫的效率。
7. 游戏开发:在游戏开发中,可以使用多线程实现游戏的并行处理,提高游戏的运行速度和响应速度。
8. 大数据处理:在大数据处理中,可以使用多线程实现并发的数据处理,提高大数据处理的效率。
9. 并发服务器:在服务器开发中,可以使用多线程实现并发的请求处理,提高服务器的并发能力。
10. 并发任务调度:在任务调度中,可以使用多线程实现并发的任务执行,提高任务的执行效率。
在实际应用中,多线程不仅可以提高程序的执行效率和响应速度,还可以充分利用多核处理器的优势,实现并行计算和并发处理。
然而,多线程编程也面临着诸多挑战,如线程安全、死锁、资源竞争等问题,需要设计合理的线程同步和互斥机制,确保程序的正确性和稳定性。
因此,在使用多线程编程时,需要仔细考虑线程间的依赖关系和数据共享问题,合理规划线程的数量和调度策略,确保多线程程序的正确性和性能。
RocketMQ是阿里巴巴开源的一款分布式消息中间件,它的多线程消费原理基于消息的有序性和消息队列的分区。
在发送消息时,RocketMQ可以通过指定消息的队列或者分区来保证消息的有序性。
消费者在消费消息时,可以通过启动多个线程来实现消息的并发消费。
具体来说,RocketMQ将Topic分为若干个分区,每个分区内的消息是有序的。
在消费者端,RocketMQ为每个Topic的每个分区都维护了一个消费队列,当消费者订阅了Topic后,RocketMQ会将该Topic 的每个分区中的消息分别放入对应的消费队列中。
然后,RocketMQ通过多线程的方式并发地从这些消费队列中取出消息进行消费。
每个线程独立消费一个队列或者分区的消息,这样可以有效地提高消息的处理能力。
同时,为了保证消息的有序性,RocketMQ限制了同一时间只有一个线程可以消费一个特定的队列或者分区。
因此,RocketMQ的多线程消费原理可以概括为:通过将Topic 划分为多个分区,并将每个分区的消息放入独立的消费队列中,然后通过多线程并发地从这些消费队列中取出消息进行消费,从而提高了消息的处理能力。
同时,为了保证消息的有序性,RocketMQ限制了同一时间只有一个线程可以消费一个特定的队列或者分区。
一、概述在分布式系统中,进程间通信是非常重要的。
而zeromq作为一个高性能的消息中间件,提供了多种进程间通信的方式。
其中,IPC (Inter-Process Communication)是zeromq中较为常用的一种方式。
本文将深入探讨zeromq IPC的实现原理。
二、zeromq简介1. zeromq是一个轻量级、高性能、开源的消息中间件,提供了一系列简单易用的API,用于实现分布式系统中的进程间通信。
2. zeromq支持多种通信模式,包括REQ/REP、PUB/SUB、P本人R 等,以及多种传输协议,如TCP、IPC、inproc等。
三、IPC通信方式1. IPC,即Inter-Process Communication,是指进程间通信的方式。
在zeromq中,IPC通信方式可以用于同一台机器上的进程间通信。
2. IPC通信方式可以实现进程间的高效数据传输,同时也可以利用多核处理器来实现并行计算。
四、zeromq IPC的实现原理1. zeromq基于消息队列的方式实现了IPC通信,它采用了多线程并发、IO复用等技术来实现高效的消息传输。
2. 通信流程在zeromq IPC通信中,通信流程一般包括以下步骤:a) 服务端创建Socket并绑定到相应的位置区域b) 客户端创建Socket并连接到服务端位置区域c) 客户端向服务端发送消息d) 服务端接收消息并处理e) 服务端向客户端发送响应消息f) 客户端接收响应消息并处理3. 基于消息队列在zeromq IPC通信中,消息队列是一个核心概念。
通过消息队列,zeromq可以实现异步、无阻塞的消息传输,从而提高了通信效率。
4. 多线程并发在IPC通信中,zeromq利用多线程并发来处理多个Socket的消息传输,从而实现了高并发的能力。
5. IO复用zeromq使用了IO复用技术,来实现对多个Socket的高效监控和消息处理。
六、总结zeromq IPC作为一个高性能的消息中间件,在分布式系统中发挥着重要的作用。
android handlerthread用法Android HandlerThread是Android开发中常用的多线程处理工具。
它能够在后台线程中执行任务,并通过Handler与UI线程进行通信。
本文将详细介绍HandlerThread的用法,以及如何使用它来实现多线程任务处理。
一、什么是HandlerThread?HandlerThread是Android中的一个类,继承自Thread,并实现了Looper接口。
它在后台线程中提供了一个消息循环机制,可以用来处理耗时的任务。
二、使用HandlerThread的步骤1. 创建HandlerThread实例首先,我们需要创建一个HandlerThread的实例,并给它一个有意义的名称。
例如,可以创建一个名为"BackgroundThread"的HandlerThread 实例:javaHandlerThread handlerThread = newHandlerThread("BackgroundThread");2. 启动HandlerThread接下来,我们需要调用HandlerThread的start方法来启动它的线程:javahandlerThread.start();3. 创建Handler然后,我们可以通过HandlerThread的getLooper方法获得一个Looper 实例,并将其用于创建一个Handler对象:javaHandler handler = new Handler(handlerThread.getLooper());通过这个Handler,我们可以在后台线程中发送和处理消息。
4. 发送消息现在我们可以在UI线程中通过Handler发送消息到后台线程中进行处理。
例如,我们可以发送一个Runnable对象到后台线程中执行:javahandler.post(new Runnable() {@Overridepublic void run() {在后台线程中执行耗时任务...}});5. 处理消息在HandlerThread的内部实现中,它会不断地从消息队列中取出消息并处理。
c++线程间通信的几种方法在C++中,多线程编程是非常常见的,而线程间通信则是必不可少的。
线程间通信是指在多个线程之间共享数据或者协调操作的过程。
以下是几种C++线程间通信的方法:1. 互斥量(mutex):通过互斥量可以保证同一时刻只有一个线程可以访问被保护的资源。
当一个线程要对共享资源进行访问时,它需要先通过mutex进行加锁保护,当它完成访问后,需要将该锁释放,以便其他的线程可以访问共享资源。
2. 条件变量(condition variable):条件变量是一种用于线程间等待和通知的机制,它提供了一种线程间同步的机制,允许一个线程等待另一个线程通知它某个特定条件已经满足。
在条件变量的使用过程中,通常需要和互斥量一起使用,以确保线程安全。
3. 信号量(semaphore):信号量是一种用于线程间同步的机制,它允许多个线程在共享资源的同时进行操作,当资源被占用时,其他线程需要等待。
信号量分为二进制信号量和计数信号量两种,其中二进制信号量只有0和1两种状态,计数信号量可以有多种状态。
4. 原子操作(atomic operations):原子操作是一种用于线程间同步的机制,它是一种可以被看作是不可分割的操作,要么全部执行,要么全部不执行。
在C++11之后,C++标准库提供了一些原子操作的模板类,如atomic_bool、atomic_int等,可以实现线程安全的计数器、状态标志等。
5. 线程池(thread pool):线程池是一种将多个线程组织起来共同完成任务的机制,它可以避免线程创建和销毁的开销,提高了应用程序的性能。
线程池通常需要和任务队列一起使用,将任务添加到任务队列中,由线程池中的线程进行处理。
总之,在进行多线程编程时,线程间通信是必不可少的,这些方法各有优缺点,在实际应用中需要根据具体的场景和需求进行选择。
多线程注意事项多线程是指在一个程序中同时运行多个线程,每个线程独立执行不同的任务。
多线程的使用可以提高程序的性能和响应速度,但同时也需要注意一些问题和注意事项。
1. 线程安全性:在多线程编程中,线程与线程之间共享同一块内存空间,因此需要关注线程安全性。
如果多个线程同时访问和修改同一份数据,可能会导致数据不一致或出现竞态条件。
为了确保线程安全,可以使用同步机制,如互斥锁(mutex)、条件变量、信号量等来控制对共享数据的访问。
2. 线程同步:线程同步是保证多个线程按照一定的顺序协同工作的一种机制。
例如,如果一个线程需要依赖另一个线程的结果,则需要使用同步机制来等待另一个线程完成任务并获取结果。
常见的线程同步机制包括互斥锁、条件变量、信号量等。
3. 死锁:当多个线程相互等待对方释放资源时,可能会导致死锁。
死锁是指所有的线程都无法继续执行,程序陷入僵局。
为了避免死锁,需要合理设计线程间资源的请求和释放顺序,避免循环等待。
4. 线程优先级:线程在操作系统中会分配一个优先级,优先级高的线程会获得更多的系统资源。
但在实际开发中,不建议过分依赖线程优先级来控制线程的执行顺序,因为不同操作系统和硬件平台对线程优先级的实现方式不同。
5. 线程创建和销毁的开销:创建线程和销毁线程都需要一定的系统资源。
频繁创建和销毁线程会带来开销,所以需要根据实际需求和系统资源的限制,合理选择线程的创建和销毁时机。
6. 上下文切换开销:当一个处理器从一个线程切换到另一个线程时,需要保存当前线程的上下文状态以及加载新线程的上下文状态,这个过程称为上下文切换。
上下文切换会带来一定的开销,特别是当线程数量较多时。
因此,合理控制线程数量,避免不必要的线程切换,可以提高程序的性能。
7. 资源管理:多线程需要共享系统资源,如内存、文件、网络连接等。
因此,需要合理地管理和分配这些资源,避免出现资源争用的情况。
特别是当多个线程同时访问和修改同一份数据时,需要确保对资源的访问和修改都是线程安全的。
JAVA实现多线程处理批量发送短信、APP推送/*** 推送消息 APP、短信* @param message* @throws Exception*/public void sendMsg(Message message) throws Exception{try {("send message start...");long startTime = System.currentTimeMillis();BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(20000);ThreadPoolExecutor executors = new ThreadPoolExecutor(5, 6, 60000, TimeUnit.SECONDS, queue);//要推送的⽤户总数int count = filterPhonesCount(message);("message all count=>{}",count);//初始每个线程处理的⽤户数量final int eveLength = 2000;//计算处理所有⽤户需要的线程数量int eveBlocks = count / eveLength + (count % eveLength != 0 ? 1 : 0);("need thread's count=>{}",eveBlocks);//线程计数器CountDownLatch doneSignal = new CountDownLatch(eveBlocks);//开启线程处理int doneCount = 0;for (int page = 0; page < eveBlocks; page++) { /* blocks太⼤可以再细分重新调度 */MessageSendThread ms = new MessageSendThread(messageDao,message,page + 1,eveLength,doneSignal);executors.execute(ms);//("start thread =>{}",page+1);doneCount++;}doneSignal.await();//等待所有计数器线程执⾏完long endTime = System.currentTimeMillis();("send message all thread ends!time(s)=>{}",(startTime-endTime)/1000);("all thread count=>{}",doneCount);} catch (Exception e) {logger.error("send message error=>{}",e);}}package com.bankhui.center.business.service.message;import java.util.Arrays;import java.util.HashMap;import java.util.Map;import java.util.concurrent.CountDownLatch;import java.util.regex.Matcher;import java.util.regex.Pattern;import mons.collections.CollectionUtils;import mons.collections.MapUtils;import ng3.StringUtils;import org.apache.http.impl.cookie.DateUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import com.bankhui.center.business.dao.message.MessageDao;import com.bankhui.center.business.entity.message.Message;import mon.utils.DateUtil;import mon.utils.SmsUtils;import com.bankhui.center.jpush.JPushClient;import com.bankhui.center.jpush.JPushScheduleClient;/*** 系统消息推送线程(处理 block数据块)*/public class MessageSendThread implements Runnable{private final Logger logger = LoggerFactory.getLogger(MessageSendThread.class);private Integer currentIndex;//当前索引private Integer rows;//处理数据条数private CountDownLatch doneSignal;//处理线程条数private Message message;//消息实体private MessageDao messageDao;//DAOpublic MessageSendThread(MessageDao messageDao,Message message,Integer currentIndex,Integer rows, CountDownLatch doneSignal) {this.message = message;this.messageDao = messageDao;this.currentIndex = currentIndex;this.rows = rows;this.doneSignal = doneSignal;}@Overridepublic void run() {try {/*** ---------1.查询当前的block范围内的发送的⼿机号=>筛选⽬标客户群⼿机号---------*/Map<String,Object> smsDataMap = filterPhones(message,currentIndex,rows);if(MapUtils.isEmpty(smsDataMap)|| null == smsDataMap.get("jgAlias")||StringUtils.isBlank(smsDataMap.get("jgAlias").toString())){logger.debug("push param is null,caurse by target customers is nothing");throw new RuntimeException();}("type of target customers=>{}", message.getReceiverGroupType());(" result of filter target customers=>{}", smsDataMap);/*** ---------2.批量发送消息---------* TODO://((-?)\d{1,11}\,?){1,n} n个线程分批发送*/if("0".equals(message.getType())){//短信发送sendBatch(smsDataMap.get("phone").toString(),message);}if("1".equals(message.getType())){//APP推送if("0".equals(message.getMethod())){//实时发送sendNormal(smsDataMap);}if("1".equals(message.getMethod())){//定时发送sendDelay(smsDataMap);}}} catch (Exception e) {logger.error("send message thread exception=>{}{}{}{}",message,currentIndex,rows,e);}finally{doneSignal.countDown();//⼯⼈完成⼯作,计数器减⼀}}/*** APP实时推送* @param smsDataMap*/private void sendNormal(Map<String,Object> smsDataMap) {//0为全部发送if("0".equals(message.getReceiverGroupType())){JPushClient.appSendAll(message.getTitle(), message.getContent(), message.getId().toString(), StringUtils.isBlank(message.getLink())?"0":"1", message.getLink());}else{String[] jgAlias = smsDataMap.get("jgAlias").toString().split(",");for(String jgAlia:jgAlias){JPushClient.appSend(message.getTitle(), message.getContent(), jgAlia, message.getId().toString(), StringUtils.isBlank(message.getLink())?"0":"1", message.getLink()); }}}/*** APP定时推送* @param smsDataMap*/private void sendDelay(Map<String,Object> smsDataMap) {//0为全部发送if("0".equals(message.getReceiverGroupType())){JPushScheduleClient.createSingleSchedule(DateUtil.formatDateToStr("yyyy-MM-dd HH:mm:ss", message.getExpectTime()),message.getTitle(),message.getContent(),message.getId().toString(),StringUtils.isBlank(message.getLink())?"0":"1",message.getLink());}else{String[] jgAlias = smsDataMap.get("jgAlias").toString().split(",");JPushScheduleClient.createSingleSchedule(Arrays.asList(jgAlias),DateUtil.formatDateToStr("yyyy-MM-dd HH:mm:ss", message.getExpectTime()),message.getTitle(),message.getContent(),message.getId().toString(),StringUtils.isBlank(message.getLink())?"0":"1",message.getLink());}}/*** 批量发送消息* @param smsDataList* @param message*/private void sendBatch(String smsDataListStr,Message message){try {//批量发送⽅法使⽤异步发送if(!message.getContent().contains("退订回T")){message.setContent(message.getContent()+"退订回T");}SmsUtils.batchExecuteTask(smsDataListStr, message.getContent());//短信测试⽅法//SmsUtils.batchExecuteTask(smsDataListStr, message.getContent(),true);} catch (Exception e) {e.printStackTrace();logger.error("批量发送消息异常=>{}{}",smsDataListStr,e);}}}1/**2 * 批量发送消息3 * @param smsDataList4 * @param message5*/6private void sendBatch(String smsDataListStr,Message message){7try {8//批量发送⽅法使⽤异步发送9if(!message.getContent().contains("退订回T")){10 message.setContent(message.getContent()+"退订回T");11 }12 SmsUtils.batchExecuteTask(smsDataListStr, message.getContent());13//短信测试⽅法14//SmsUtils.batchExecuteTask(smsDataListStr, message.getContent(),true);15 } catch (Exception e) {16 e.printStackTrace();17 logger.error("批量发送消息异常=>{}{}",smsDataListStr,e);18 }19 }1public static String sendSmsCL(String mobile, String content,String urlStr,String un, String pw, String rd) {2// 创建StringBuffer对象⽤来操作字符串3 StringBuffer sb = new StringBuffer(urlStr+"?");4// ⽤户账号5 sb.append("un="+un);67//⽤户密码8 sb.append("&pw="+pw);910// 是否需要状态报告,0表⽰不需要,1表⽰需要11 sb.append("&rd="+rd);1213// 向StringBuffer追加⼿机号码14 sb.append("&phone="+mobile);1516// 返回发送结果17 String inputline;18 BufferedReader in = null;19 InputStreamReader isr = null;20try {21// 向StringBuffer追加消息内容转URL标准码22 sb.append("&msg="+URLEncoder.encode(content,"UTF8"));23// 创建url对象24 URL url = new URL(sb.toString());2526// 打开url连接27 HttpURLConnection connection = (HttpURLConnection) url.openConnection();2829// 设置url请求⽅式 ‘get’ 或者 ‘post’30 connection.setRequestMethod("POST");31 isr = new InputStreamReader(url.openStream());32// 发送33 in = new BufferedReader(isr);34 inputline = in.readLine();35if(inputline.contains(",0")){36 ("⼿机号:【{}】发送短信成功", mobile);37 }else{38 ("⼿机号:【{}】发送短信失败,errorMsg is:{}", mobile,inputline);39 }40// 输出结果41return inputline;42 } catch (Exception e) {43 logger.error("发送短信请求异常:{}", e.getMessage());44return e.getMessage();45 } finally{46if(null != isr){47try {48 isr.close();49 } catch (IOException e) {50 logger.error("关闭流异常:{}", e.getMessage());51 }52 }53if(null != in){54try {55 in.close();56 } catch (IOException e) {57 logger.error("关闭流异常:{}", e.getMessage());58 }59 }60 }6162 }package com.bankhui.center.jpush;import java.util.HashMap;import java.util.Map;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/*** The entrance of JPush API library.**/public class JPushClient extends BaseClient {private static Logger logger = LoggerFactory.getLogger(JPushClient.class);//在极光注册上传应⽤的 appKey 和 masterSecretprivate static final String appKeyStr ="******************";////必填,private static final String masterSecretStr = "******************";//必填,每个应⽤都对应⼀个masterSecretprivate static JPushClient jpush = null;/** 保存离线的时长。
消息堆积的解决方法主要包括以下几个方面:
增加系统处理能力:可以通过增加硬件资源、使用多线程或多进程、优化算法等方式提高系统的处理速度和能力,从而减少消息的堆积。
优化网络性能:通过升级网络带宽、优化网络拓扑结构和数据传输协议,可以加快消息的传输速度,降低消息的积压数量。
过期处理:对于一段时间内未被处理的消息,可以进行过期处理,以避免堆积影响系统的性能和可靠性。
消息队列管理:通过合理地管理消息队列,可以提高系统的响应速度和可靠性,避免消息积压的问题。
采取消息控制策略:如果消息数量太多,需要采取一些控制策略,如限制消息的发送频率、数量和大小等,以保持系统的稳定性。
扩容消费者群组:如果仅仅是消费者的处理速度落后于消息的生产速度,可以考虑扩容消费者群组,以加快消息的处理速度。
临时处理积压消息:如果积压的消息非常多,可能需要临时写一个程序来消费这些积压的消息,然后恢复原有的架构。
补数据:如果因为消息丢失导致数据不完整,可以写一个临时程序来重新灌入丢失的数据。
根据具体情况选择适合的方法解决消息堆积问题,可以提高系统的性能和可靠性。
多线程下的消息处理北京国防科工委指挥技术学院(101407) 钱 明 张盛蕾 付长东
摘 要:说明在W indow s95环境下,如何实现多线程间的消息传递,并给出线程间消息传递的具体方式。
关键词:消息 线程 同步 窗口
概 述
W indow s95、W indow s N T、M ach等操作系统都以线程(th read)作为系统的调度和运行单位,线程是比进程粒度更细的并行机制,它是程序内的动态执行流[4]。
1个任务内可以有多个线程并行运行,它们共享任务的所有资源,如内存空间。
利用多线程可实现任务内的各子任务并行运行,提高事务处理效率。
同任务内的多线程间可通过全局变量、文件、信号灯等方式实现同步。
1个线程使自己处于睡眠(sleep)状态,等待所需要的事件发生,并由系统核心唤醒启动它。
虽然此方式能实现同步多线程间的并行运行,但它不符合W in2 dow s程序结构,W indow s系统的程序结构是以消息驱动方式构成的,主程序的消息循环部分从消息队列中获取与本程序有关的消息,并且送至相应的窗口过程函数,窗口过程函数负责接受并处理消息。
如何在多线程间以消息传递方式来同步它们的并行运行,是值得讨论的问题。
即每个线程都通过获取消息来驱动程序的运行,它们之间通过相互传递消息实现并行同步。
当然实际中,由于对大量数据的共享,使得多线程间必须使用信号灯等同步方式来解决互斥问题。
本文主要讨论关于多线程下消息处理的线程消息队列问题,线程管理问题,线程间消息传递问题以及所需的数据结构,最后还给出了线程消息处理模型。
1 关键问题
111 线程消息队列
在W indow s95下,每个任务都有自己的原始输入消息队列,它们彼此存放着相应任务的消息[1]。
对于任务内的每个线程,它可以有自己的输入队列,也可以没有。
当它调用U SER32或GD I32函数时,系统为这个线程创建1个输入消息队列,简单地说,当线程创建1个窗口时,系统相应地为这个窗口关联1个输入消息队列。
每个线程可以不使用自己的输入队列,在没有自己的输入队列的线程时,它可以共享任务的输入队列,也可以使用主线程(W in M ain())的输入队列。
它的模型如图1所示。
112 数据结构
当主线程创建子线程时,它调用C reateT h read()
图1线程消息模型图
消息队列
窗口
主线程
子线程子线程子线程
消息队列
窗口
函数,成功返回时,需要保存2个参数:线程句柄和线程标识符。
另外,由于每个线程可能有自己的窗口,因而需保存窗口的句柄。
如果线程没有创建窗口,则将主线程的窗口句柄设置为它的缺省值。
需要的数据结构如下:
struct userT h read{
HWND hw nd;
HWND h th read;
DWORD th readid;
}CT h reaad[M A XNUM];
数组CT h read[0]填写主线程的信息,它通过GetCu rren tT h read(),GetCu rren tT h read Id()获得主线程的线程句柄与线程标识符。
当创建线程时,依次将每个线程信息填入CT h read[n](n=1~M A XNUM)中。
当然数组CT h read是全局变量,它可被所有线程共享,因此我们将n(1~M A XNUM)参数作为传递给线程的参数,每个线程通过参数n(定值)定位自身的CT h read信息,其关系如下:
子线程
将N作为传递参数
填写Cthread[N]
创建的第N个子线程由N定位本线程的信息
2 多线程的消息传递
以图1作为讨论的模型,它能够表示多线程消息传递的一般情形。
根据模型,需要讨论以下几种消息传递情形:
主线程〈——〉子线程1,它们都带有窗口;
主线程〈——〉子线程2,子线程没有窗口;
子线程1〈——〉子线程2,都是子线程,1个没有窗口;
子线程2〈——〉子线程N,都是子线程,都没有窗
口;
多线程下的消息传递可以采用以下2种方法:窗口方式和线程方式。
211 窗口方式
主线子线1;主线子线2;子线1子线2; 窗口方式只能是向所有窗口的线程发送消息,对于没有窗口的线程,不能采用此方法。
因而只有以下情形才能采用此
方式(将子线程N 和子线程2作为同一种情况)。
窗口方式的一般处理过程如下:
对于接受消息的线程,完全不需要改变它的程序结构,只要在窗口过程函数中增加对应的消息处理即可。
而对于发送消息的线程,它将接受消息线程的窗口句柄作为参数,利用Po st M essage ()函数,发送相应的消息。
212 线程方式
线程方式适用于除第4种情形以外任何情形(第4种情形实际上是不直接的),但它必须改动接受消息线程的程序结构。
其处理过程如下:
发送消息的线程以接受消息线程的线程句柄为参数,利用Po stT h read M essage ()函数发送相应的消息。
接受消息的线程程序结构作如下改动:
①符合W indow s 程序结构的改动
主程序{
……
w h ile (Get M essage (&m sg ,NULL ,0,0)){ if (m sg .m essage ==1个特定消息){
Po st M essage (hw nd ,m sg .m essage ,m sg .w Param
m sg .lParam ); } else {
T ranslate M essage (&m sg ); D ispatch M essage (&m sg ); } }
…… }
因为Po stT h read M essage ()不是以窗口句柄为参数,因而接受消息的主程序不可能将消息送至相应的窗口过程函数,必须将消息转换为以窗口为参数的消息传递。
②没有窗口的改动
对于子线程2及子线程N 而言,它们没有窗口,不需要W indow s 程序结构,但要接受相应的消息,需加入以下部分,以符合消息驱动程序。
DWORD m yth readN (int N um ber ) {…… W h ile (Get M essage (&m sg ,NULL ,0,0)){ s w itch (m sg .m essage ){ case xx : ……
case xx : …… } } …… }
当然,程序可以不采用上述消息驱动模式的处理方式。
可以在需要消息的时候,调用Get M essage ()获得发送给子线程的消息。
3 消息处理模型
本节主要说明利用消息传递方式实现多线程同步的一般处理模型。
息消
图2线程消息传递模型窗口主线程窗口子线程1子线程2子线程N
如图2所
示,因为没有
窗口的子线程
之间不可以用
任何方式直接
相互传递消息,所以它必须先向主线程发送消息,而由主线程再向其它的子线程转发消息。
我们规定:(1)所有子线程和子线程之间不可相互发送消息,必须经过主线程,使主线程成为管理者线程,它负责转发和处理工作;(2)对于有窗口的线程,传递消息采用窗口方式。
对于没有窗口的线程采用线程方式。
因而子线程向主线程传递消息,肯定采用统一的窗口方式。
为了标识子线程之间的相互传递,必须给出目标子线程,这样主线程才能正确传发。
另外还必须标识发送给主线程并由主线程处理的消息。
因此我们规定(3)在利用Po st M essage ()向主线程发送消息时,如果是发送给主线程并由主线程处理的消息,消息内容不许改变。
而如果是转发的消息,将lParam 设置为-1,而将w Param 设置为目标线程号(参数n )。
因而在主线程消息处理中必须判断
lParam 的值。
显然,实现这种统一简便的处理模型是以
子线程间相互传递消息时,只能标识消息而不能再由w Param 、lParam 说明更多信息为代价的。
我们利用此模型已实现了基于线程的分布式测控系统的前端系统处理软件。
由于多线程设计程序是比较新颖的,且多线程间可利用消息相互同步,因此本文提出此种方法希望给同行们一点启示。
参考文献
1 [美]M .A ndrew s 、W indow s N T 315与V C ++程序设计1
北京:科学出版社,1995,1
2 [美]H .Custer 1W indow s N T 技术内幕1北京:清华大学
出版社,1993,7
3 [美]D .K i pp ing .W indow s 95编程指南1北京:学苑出版
社,1995,8
4 P rogramm ing under M ach .1994,8
(收稿日期:1996212229)。