电脑桌面
添加盘古文库-分享文档发现价值到电脑桌面
安装后可以在桌面快捷访问

异步多线程范文

来源:文库作者:开心麻花2025-09-191

异步多线程范文(精选7篇)

异步多线程 第1篇

远程抄表系统近年来得到日益广泛的应用,物业管理公司可以用它来节省人工抄表的人力,建筑节能系统需要它来提供用能情况的数据。基本要求如下:建筑中有若干个采集器,每个采集器连接若干个电表,采集器通过局域网上报电表读数,每两分钟一次,数据格式为XML。采集器定时连接服务器的SOCKET端口发送一个包含采集器编号及各电表读数的数据包,发送完成后自动断开。服务器端接收并显示最后接收的数据,同时写入SQL数据库。

2 实现过程

做程序第一个版本的时候想的比较简单,采取的是同步接收方式,开一个线程监听端口,接收到数据就进行数据处理并显示,然后断开连接重启监听。但实际进行测试的时候发现多台采集器近乎同时发送数据时会有丢包现象。采集器认为数据正常发送了,但服务器程序没有收到。经过对比、分析,笔者认为是两次数据发送间隔时间不够造成的,但把数据处理过程放在重启监听之后还是无法避免丢包。于是,只好放弃同步接收,转为多线程异步接收。

3 程序结构

启动界面包括:进行本机IP、监听端口、SQL数据库连接串设置的三个TextBox,开始、中止两个Button,显示已接收数据的RichTextBox,显示接收数据内容的DataGridView,显示运行状态的Label。

按开始按钮会启动一个监听线程Listener。

监听线程每收到一个连接请求就会启动一个接收线程ReceiveWorkThread。

接收线程将接收到的数据前面加上接收时间显示在RichTextBox里,再调用数据处理函数disp_info来显示数据内容并写入数据库。

4 代码说明

4.1 变量定义

4.2 启动界面

本程序采取了XML记录初始化信息的方式。

布局如图1所示。

4.3 启动监听

4.4 监听线程

4.5 接收线程

5 结语

接收程序改为多线程异步接收方式后,经测试无丢包现象。通过此次实践笔者认为多线程异步Socket接收并不像想象中那么复杂,只要弄清楚各线程的启动顺序、互相之间的关系就可以很容易地实现各种应用。当然,其中还是有些细节问题需要读者在实际应用中注意的,如:数据接收线程中无法直接在启动界面中显示数据(因为同一线程)、如何把DataGridView中数据直接写入数据库等。

异步多线程 第2篇

在实现异步调用机制时, 除了线程之外, 还要用到回调。回调是一种双向调用, 也就是, 被调方在被调用时也会调用主调方的代码。在异步调用中, 被调方需要在工作完成时通知主调方, 即调用主调方的接口, 这一机制通过回调实现。回调和异步调用的关系非常紧密, 回调是异步调用的基础[1]。

本文理论联系实际, 首先阐述如何使用Java实现回调机制, 然后进一步阐述使用Java回调和线程实现异步调用, 最后, 阐述在异步调用中如何处理超时问题。

1 Java回调机制的实现方法

实现Java回调, 需要做如下三件事情:

(1) 定义一个回调接口Callback Interface

接口中声明回调方法handle, 如图1所示, 回调方法就是一个普通的方法, 接收一个消息字符串或者一个封装了数据的事件。

(2) 定义一个类实现回调接口

这个类其实就是消息接收者和处理者, 如图2所示, 其中:回调方法是消息发生时实际处理消息的方法, 此处简化为一条打印语句。

(3) 定义消息通知者

消息通知者必须具备两种能力, 第一, 它必须知道谁是消息接收者, 第二, 当消息发生时, 它能够回调这些接收者的回调方法。为了获得这两种能力, 消息通知者首先必须提供一个注册方法register, 通过注册的方式来注册多个对此消息或事件感兴趣的对象。然后提供一个消息通知方法notify Message, 在这个方法中调用所有消息接收者的回调方法。具体代码如图3所示。

上述代码使用了一个可变数组List用于保存消息接收者, 注册的过程实际上是将消息接收者添加到这个数组, 以备在需要通知消息的时候调用这些消息接收者的回调方法。

有了消息接收者和消息通知者, 就可以完成消息通知机制了, 图4给出的是测试代码。

这里有一个非常关键的步骤就是将消息接收者向消息通知者注册, 只有已注册的对象才会收到消息通知。

值得一提的是, Java回调机制的使用非常广泛, 比如Java的事件监听器模式和观察者模式都包含了回调机制[2], 在一些框架API提供的调用中不少也是以回调的形式提供的[3]。深刻理解Java回调机制是很有裨益的。

运行结果如图5所示。

2 使用Java回调和线程实现异步调用

线程是一个独立的执行流, 其本质是程序中一段并发执行的代码。在异步调用机制中引入线程, 在线程中完成耗时的工作, 其目的是让调用方的主线程继续执行后续代码而不需要等待被调方的结果返回。由于不需要等待, 这样我们就等于同时做了两件事情, 而这两件事情分别是在不同的执行流中执行, 主调者在当前的主线程中执行, 被调者在另外一个线程中执行, 因此提高了程序的效率, 避免了界面的响应迟钝。当被调者执行完成后, 仍然采用回调通知主调者。

如图6所示, Long Time Worker是一个用于完成耗时工作的线程, 同时又是消息通知者。其耗时工作在run方法中完成, 另外提供一个注册方法register, 和一个消息通知方法notify Message, 在run方法的最后, 即耗时工作完成以后, 调用notify Message将消息广播出去。

将前面的测试代码做一点改动就可以看到异步调用的效果如图7所示。

3 异步调用中超时问题的处理

异步调用通常都要加入超时机制, 因为我们总是希望在一个指定的时间范围内返回一个结果, 即使没有得到结果也该有个超时通知。这时我们需要使用“限时线程回调方式”, 它在原有线程回调的基础上加上一个计时器Timer以计算消耗的时间, 如果时间期限到了任务还没有执行完成即中断线程, 并将超时消息广播出去。Long Time Worker类需要修改部分的代码如图8和图9所示。

首先Long Time Worker线程类增加了一个构造方法, 其参数是超时时间timeout, 构造方法的主要任务是创建一个定时器, 每秒钟计时一次, 若超时时间到则终止本线程, 并广播超时消息。Long Time Worker线程类的第二个改变发生在其run方法中, 线程一启动立即开始计时, 完成工作后停止计时, 并广播消息。

4 结束语

异步调用是一种非阻塞式调用方式, 用于在处理比较耗时的任务时保证程序性能不受到影响。实现异步调用的关键在于要解决三个技术难题, 它们分别是程序阻塞问题、异步消息的传递问题和超时问题。本文介绍的方法采用并发线程、回调机制和计时器使上述问题得到了圆满解决。

摘要:异步调用是一种非阻塞式调用方式, 用于在处理比较耗时的任务时保证程序性能不受到影响。实现异步调用的关键在于要解决三个技术问题, 它们分别是程序阻塞问题、异步消息的传递问题和超时问题。本文介绍的开发方法和步骤采用并发线程、回调机制和计时器圆满地解决了异步调用的技术难题。

关键词:程序阻塞,异步消息传递,Java回调,线程,异步调用

参考文献

[1]陈家朋.异步消息的传递-回调机制[EB/OL].http://www.ibm.com/developerworks/cn/linux/l-callback/, 2003.

[2]Eric Freeman.Head First Design Pattern[M].O’Reilly Media, Inc.2004:51-53.

异步多线程 第3篇

随着并行技术的不断发展,如何更好地提高处理器的并行处理性能成为处理器设计者们急需解决的热点问题。 传统处理器通过开发指令级并行(Instruction Level Parallelism , ILP )[1]来提高处理器的性能,但由于应用程序自身的ILP很低、 硬件的复杂度及功耗等众多因素的影响导致处理器的性能不高。 因此设计者们把目光纷纷投向更高层次的并行- 线程级并行( Thread Level Parallelism , 简称TLP )[1,2]。

同时多线程最先由美国加州大学的Tullsen等人在1995 年开始研究, 并提出了基本的SMT[3,4]处理器模型。主要原理是通过资源竞争和资源动态共享的方式使所有的执行单元同时活跃,充分利用TLP和ILP来提高处理器的资源利用率。

目前的商用处理器采用的技术主要是同时多线程和片上多处理器技术, 例如Intel的P4 处理器[5]的Hyper - thread技术实现同时执行两个线程, IBM的Power5处理器[6]每个芯片有两个内核, 每个内核可以同时执行两个线程。 SUN的Niagara处理器[7]每个芯片有8 个内核,每个内核可以同时执行4 个线程。 我国的龙芯2 号处理器[8]也采用了超标量与同时多线程技术来设计。

本文在多核处理器[9,10,11]的基础上提出了一种采用同时多线程技术[3,4]的线程调度器[12]设计。 与Niagara处理器相比,本文中的多线程处理器拥有16 个核,每个内核一共具有8 个线程,最大可以同时执行4 个线程,并且可以在高低线程之间快速切换,减少了因为线程中遇到长周期指令所造成的资源浪费,同时也提高了处理器的利用效率。

1 整体硬件结构

时钟共享多线程处理器是一种适用于图形和图像处理的并行阵列机。 该阵列机支持MIMD(Multiple Instruction Stream Multiple Data Stream ) 运行模式、 分布式指令并行模式和流处理运行模式这三种工作模式,同时还具有异步执行、核间通信以及线程间通信的机制。

这种结构的阵列机由16 个处理单元(Processing Element , PE ) 互连构成一个4 × 4 的二维阵列, 还包括1 个前端处理器、4 个协处理器、2 个调度器及2 个存储管理。系统的整体结构如图1 所示。

2 线程调度器功能描述

线程调度器完成的功能描述如下:

( 1 ) 监测预处理模块, 当外部需要给线程加载指令或数据时响应预处理模块;

( 2 ) 当线程发生阻塞时负责切换线程, 并产生PE某一个线程的启动信息, 其中包括启动有效信号、 起始PC值、 启动的线程号;

( 3 ) 线程之间的同步处理, 负责监视需要同步的线程是否同步结束;

( 4 ) 当外部需要重新配置某一个线程的指令或数据时负责停止该线程,产生pe_stop信号;

(5) 给外部提供三个8 位的寄存器, 分别为线程启动信息寄存器、线程结束信息寄存器、线程断点信息寄存器。

3 同时多线程设计方案

同时多线程技术最大的优点在于增加很少的面积,就能够获得很高的资源利用率,即用较小的成本换取较大的并行性能,充分地挖掘了线程级的并行。

本文基于时钟共享多线程处理器提出的线程调度器支持八个线程,最多可以同时执行四个线程。 这四个线程同时执行各自的程序。 每个线程拥有各自独立的取指单元、 译码单元、 指令存储和数据存储。 而其他的资源,如各个执行单元和地址流水线,则是八个线程共享。

在每一个时钟周期的上升沿, 每个PE最多有八条流水线在执行, 且最多可输出四条流水线结果, 这四条流水线产生的结果属于不同的线程组,每个线程组都拥有高低两个线程。 因此在某一组中若有一个线程陷入阻塞状态, 则可以切换至组内的另一线程来执行程序,可以有效隐藏线程由于等待过程所造成的延迟,提高处理器整体的工作效率。

4 线程调度器详细设计

4 . 1 线程管理器的总体结构

该调度器由信息处理模块、 状态控制模块、 寄存器模块、PE控制模块四大部分构成,各模块的连接关系如图2 所示。

线程调度器的工作流程为: 信息处理模块接收来自预处理模块的配置信息、指令、数据及加载结束信号,判断是否需要启动该线程。 线程启动之后状态控制模块中对应的线程状态机发生跳转,同时寄存器模块实时记录该线程的PC值。 当接收到译码模块的阻塞信息时停止当前运行的线程且切换线程,PE控制模块负责产生新线程的启动信息。 当线程运行结束后,信息处理模块负责记录线程的结束信息。

4 . 2 线程调度器的工作模式

线程调度器拥有五种工作模式: 启动模式、 阻塞工作模式、同步模式、断点模式和重配置模式。

启动模式线程接收来自预处理模块的线程启动信息配置要启动的线程,将对应的线程ID号和PC值发送给PE来执行。

阻塞工作模式表示当某一线程遇到阻塞, 则切换到其线程组内的另一线程上来执行。 若是两个都阻塞,则先解除阻塞的线程先执行。

同步模式下对需要同步的几个线程进行等待处理,并判断是否达到同步。 若达到,则解除同步模式,开始正常执行;若没有达到,则继续等待。

断点模式是用来方便处理器进行调试错误的一种工作模式。 当程序计数器PC遇到断点时, 该线程停止工作,保存工作信息以待调试检查。

重配置模式是对线程进行重新配置, 根据上层的需要对线程要执行的程序内容进行添加或更改。

4 . 3 线程状态转移关系

如图3 所示,为线程调度器中线程的状态转移图。

其中各个状态说明如下:

( 1 ) IDLE : 空闲状态。 当线程启动信号有效时( th_start = 1 ) , 则状态跳转到RUN运行状态;

( 2 ) RUN : 运行状态。 当线程位于此状态时, 表示线程正在执行,遇到阻塞或等待跳转到WAIT状态;

( 3 ) BREAK : 断点状态。 当线程的断点数据收集结束( th_break_end = 1 ) 时, 状态跳转到RUN运行状态;

( 4 ) WAIT : 等待状态。 当线程发生重配置( th_recfg = 1 )时, 则状态跳转到IDLE初始状态; 当线程阻塞解除( th_blk_fns = 1 ) 时状态跳转到RUN运行状态;

( 5 ) SYN : 同步状态。 线程位于该状态下, 遇到同步解除(th_syn_fns=1)信号,跳转到READY状态;

( 6 ) READY : 准备状态。 表示线程处于一个随时可以启动的状态,当线程被选中时(th0_hit=1),则状态跳转到RUN运行状态。

5 仿真结果分析和比较

本文采用Xilinx公司的ISE14.4 工具对硬件电路进行了综合, 选用Xilinx公司型号为XC7V2000t-2fhg1761的FPGA,设计电路的最高频率可以到达431.816 MHz。

在完成功能仿真和综合的同时, 本文还进行了简单的性能分析。 测试在8 个PE上进行, 采用图像处理中的边缘检测算法, 分别对32×32、64×64 以及128×64 的图像进行测试,最后得到的结果如表1 所示。 另外根据性能提升计算公式(1)[12]可以得出整体处理器的性能提升百分比如表2 所示。

可以看出, 对于比较小的图像, 过多的线程会导致整体运算速度变慢。 这是因为此时图像整体运算时间比较短,线程切换所造成的延时会在整体时间中占据比较大的比例。 而对于更大的图像,同时多线程技术则对处理器的性能提升影响比较大。 对于多核同时多线程处理器而言,八个线程的线程调度器设计会使处理器得到更高的性能提升,充分体现了同时多线程技术的优点。

6 总结

本文通过对时钟共享多线程处理器架构的深入研究,采用同时多线程技术设计和实现了处理器的核心部件 ——— 线程调度器。 通过在处理器的多个线程上运行图像处理算法,得到处理器的加速比最大为69.25% , 充分提高了多核处理器的性能。 并对所设计的硬件电路进行了全面仿真验证, 综合和仿真结果表明电路的功能正确,工作频率为431.816 MHz,达到了时钟共享多线程处理器的需求。

参考文献

[1]THEO U,BORUT R,JURIJ S.Multithreaded processors[J].The Computer Journal,2002,45(3):320-348.

[2]BRUNIE N,COLLANGE S,DIAMOS G.Simultaneous branch and warp interweaving for sustained GPU performance[C].Computer Architecture(ISCA),2012:49-60.

[3]刘权胜,杨洪斌,吴悦.同时多线程技术[J].计算机工程与设计,2008,29(4):963-967.

[4]EGGERS S J,EMER J S,LEVY H M,et al.Simultaneous multithreading:A platform fornext-generation processors[J].IEEE Micro,1997,17(5):12-19.

[5]张云.解析超线程技术[J].甘肃联合大学学报,2007,21(4):99-101.

[6]Frank Soltis.Power5对i系列意味着什么[J].中国经济和信息化,2004,20(6):A2-A3.

[7]POONACHA K.Niagara:a 32-way multithreaded sparc processor[J].Mirco,IEEE,2005,25(2):21-29.

[8]李祖松,许先超,胡伟武,等.龙芯2号处理器的同时多线程设计[J].计算机学报,2009,32(11):2266-2273.

[9]黄虎才.多态阵列处理器的并行计算研究[D].西安:西安邮电大学,2014.

[10]李涛,杨婷,易学渊.萤火虫2:一种多态并行机的硬件体系结构[J].计算机工程与科学,2014,12(2):191-200.

[11]李涛,肖灵芝.面向图形和图像处理的轻核阵列机结构[J].西安邮电学院学报,2012,17(3):41-47.

Java多线程机制 第4篇

程序是一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。而进程则不同,它是程序在某个数据集上的执行。进程是一个动态的实体,它有自己的生命周期。它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消,反映了一个程序在一定的数据集上运行的全部动态过程。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程本身基本上不拥有系统资源,但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。所以线程是比进程更小的执行单位,一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、存在和消亡的过程,也是一个动态的概念。

Java的多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。在操作系统每次分时给Java程序一个时间片的CPU时间内,在若干个独立的可控制的线程之间切换。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此,只需要设想各个线程是同时执行即可。线程需要操作系统的支持,不是所有类型的计算机都支持多线程应用程序。Java程序设计语言将线程支持与语言运行环境结合在一起,提供了多任务并发执行的能力。这就好比一个人在处理家务的过程中,将衣服放到洗衣机中自动洗涤,将大米放在电饭锅里,然后开始做菜。等菜做好了,饭熟了同时衣服也洗好了。

1 线程的状态与生命周期

一个线程从创建、启动、运行到完毕称为一个线程的生命周期。新建的线程在它的一个完整的生命周期中通常要经历如下5种状态。

(1)新建状态

当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态,此时它已经有了相应的内存空间和其他资源。

(2)就绪状态

线程创建之后仅仅是占有了内存资源,在JVM管理的线程中还没有这个线程,此线程必须调用start()方法(从父类继承的方法)通知JVM,这样JVM就会知道又有一个新线程排队等侯切换了。

(3)运行状态

线程创建之后就具备了运行的条件,一旦轮到它来享用CPU资源时,即JVM将CPU使用权切换给该线程时,此线程就可以脱离创建它的主线程独立开始自己的生命周期了(即run()方法执行的过程,该方法规定了线程的具体使命)。

(4)阻塞状态

一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态。有4种原因的阻塞:CPU资源从当前线程切换给其他线程、执行了sleep(int millsecond)方法、执行了wait()方法、执行某个操作进入阻塞状态(如执行读/写操作引起阻塞)。

(5)终止状态

处于死亡状态的线程不具有继续运行的能力。线程死亡的原因有二:一个是正常运行的线程完成了它的全部工作,即执行完了run()方法的最后一个语句并退出;另一个是线程被提前强制性的终止,即强制run()方法结束。

2 线程调度与优先级

对于多线程程序,每个线程的重要程度不尽相同,如多个线程在等待获得CPU时间时,往往需要优先级高的线程优先抢占到CPU时间得以执行;又如多个线程交替执行时,优先级决定了级别高的线程得到CPU的次数多一些且时间长一些。这样,高优先级的线程处理任务的效率就高一些。

在Java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程)。一个经常犯的错误是认为“抢占”就是“分配时间片”。在Solaris平台的运行环境中,相同优先级的线程不能相互抢占对方的CPU时间。但是,在使用时间片的Windows平台运行环境中,可以抢占相同甚至更高优先级线程的CPU时间。抢占并不是绝对的,可是大多数JVM的实现结果在行为上表现出了严格的抢占。纵观JVM的实现,并没有绝对的抢占或是时间片,而是依赖于编码者对wait()和sleep()这两个方法的使用。

抢占式调度模型就是许多线程属于可以运行状态,但实际上只有一个线程在运行。该线程一直运行到它终止进入可运行状态或是另一个具有更高优先级的线程变成可运行状态。在后一种情况下,低优先级的线程被高优先级的线程抢占,高优先级的线程获得运行的机会。

线程可以因为各种各样的原因进入阻塞状态。例如,线程的代码可以在适当时候执行Thread.sleep()方法,故意让线程中止;线程可能为了访问资源而不得不等待直到该资源可用为止。所有可运行的线程根据优先级保持在不同的池中,一旦被阻塞的线程返回可运行状态,它将会被放回适当的可运行池中。非空最高优先级池中的线程将获得CPU时间。JVM中的线程调度器负责管理线程,调度器把Java中线程的优先级从低到高以整数1~10表示,共分为10级,设置优先级是通过调用线程对象的setPriority()方法实现的。

3 实现多线程的方法

Java是面向对象的程序语言,用Java进行程序设计就是设计和使用类,Java提供了线程类Thread来创建线程,创建线程与创建普通类的对象操作是一样的,而线程就是Thread类或其子类的实例对象。

Thread thread1=new Thread();

//声明一个对象实例,即创建一个线程

thread1.run();

//用Thread类中的run()方法启动线程

从第一句看到,可以通过Thread()构造方法创建一个线程,并启动该线程。事实上,启动线程,也就是启动线程的run()方法,而Thread类中的run()方法没有任何操作语句,所以这个线程没有任何操作。要使线程实现预定功能,必须定义自己的run()方法。Java中通常有两种方式定义run()方法:

(1)继承Thread类,覆盖方法run()

在创建的Thread类的子类中重写run(),加入线程所要执行的代码即可。这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果类已经从一个类继承(如小程序必须继承自Applet类),则无法再继承Thread类,这时如果又不想建立一个新的类,应该如何处理。

不妨来探索一种新的方法:如果不创建Thread类的子类,而是直接使用它,那么只能将方法作为参数传递给Thread类的实例,有点类似回调函数。但是Java没有指针,只能传递一个包含这个方法的类的实例。如何限制这个类必须包含这一方法,当然是使用接口(虽然抽象类也可满足,但是需要继承,而之所以要采用这种新方法,就是为了避免继承带来的限制)。Java提供了接口java.lang.Runnable来支持这种方法。

(2)实现Runnable接口

Runnable接口只有一个方法run(),声明自己的类实现Runnable接口并提供这一方法,将线程代码写入其中,就完成了这一部分的任务。但是Runnable接口并没有任何对线程的支持,还必须创建Thread类的实例,这一点通过Thread类的构造函数来实现。

如果是创建Thread类的实例,必须注意的是,该子类必须没有覆盖Threa类的run()方法,否则该线程执行的将是子类的run()方法,而不是用以实现Runnable接口的类的run()方法。

使用Runnable接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装。它的缺点在于,只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类。如果这样的话,在大多数情况下也许还不如直接用多个类分别继承Thread来得紧凑。

4 线程的同步

由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

由于可以通过private关键字来保证数据对象只能被方法访问,只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块。

(1)synchronized方法

通过在方法声明中加入synchronized关键字来声明synchronized方法。如:

public synchronized void accessVal(int newVal);

synchronized方法控制对类成员变量的访问:每个类实例对应一把锁,每个synchronized方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞。方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为synchronized的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为synchronized)。

在Java中,不光是类实例,每一个类也对应一把锁,这样也可将类的静态成员函数声明为synchronized,以控制其对类的静态成员变量的访问。synchronized方法的缺陷:若将一个大的方法声明为synchronized,将会大大影响效率。若将线程类的方法run()声明为synchronized,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何synchronized方法的调用都永远不会成功。当然可以通过将访问类成员变量的代码放到专门的方法中,将其声明为synchronized,并在主方法中调用来解决这一问题。但是,Java提供了更好的解决办法,那就是synchronized块。

(2)synchronized块

通过synchronized关键字来声明synchronized块。语法如下:

synchronized(syncObject){

//允许访问控制的代码

}

synchronized块是这样一个代码块,其中的代码必须获得对象syncObject(如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

5 结束语

由于Java的多线程功能齐全,各种情况面面具到,它带来的好处也是显然易见的。多线程带来的更大的好处是更好的交互性能和实时控制性能。当然实时控制性能还取决于系统本身(UNIX、Windows、Macintosh等),在开发难易程度和性能上都比单线程要好。当然一个好的程序设计语言肯定也难免有不足之处。由于多线程还没有充分利用基本OS的这一功能,对于不同的系统,上面的程序可能会出现截然不同的结果,这使编程者偶会感到迷惑不解。希望在不久的将来Java的多线程能充分利用操作系统,减少对编程者的困惑。

参考文献

[1]耿祥义,张跃平.Java2实用教程(第三版)[M].北京:清华大学出版社,2006,8.

DELPHI中多线程研究 第5篇

一个操作系统有多个进程在同时进行, 而一个进程又会有多个线程在同时进行, 每个线程都有自己的执行状态和独立的上下文结构 (保存在线程控制块中) 及执行栈 (用户栈、系统栈) , 同一进程中的线程通过各种同步机制 (如临界区、事件、互斥量、信号灯等) 来实现对共享资源的访问。

二、Delphi中的多线程机制

Delphi编译环境的核心是可以直接调用几乎所有的Windows API函数。通常是通过过程调用一系列外部模块来实现, 其最大的优点是利用面向对象的技术支持。通过对Delphi中类实现的源代码分析, 可以从中了解到类的构造过程及功能的实现, 以便更有效的利用其提供的线程类完成多线程程序设计。

Delphi中多线程技术的实现是通过TThread类来封装Windows API的有关线程操作的编程接口。TThread类继承自TOb-ject, 除继承父类的成员外还定义了一些属性和方法, 主要分为线程对象属性、线程对象方法、线程对象事件处理三类:

(1) 线程对象属性 (Properties) :

Fatal Exception异常处理对象

Free On Terminate布尔量, 决定线程结束时是否清除

Handle线程句柄

Priority线程优先级

ReturnValue线程返回值

Suspended布尔量, 判断线程是否已挂起

Terminated布尔量, 判断线程是否需要结束

ThreadID线程全局唯一的标记

(2) 线程对象方法 (Methods) :

After Construction对象创建后运行, 重载自父类

Create创建线程对象构造器

Destroy释放线程对象析构器

Do Terminate释放线程前调用用户的清除例程

Execute线程执行, 虚类函数, 子类需重载

Resume使线程重新执行

Suspend挂起运行线程

Synchronize线程间操作同步

Terminate置线程终止标记

Wait For等待线程结束

(其它继承自父类TObject对象)

(3) 线程对象事件处理 (Events) :

onTerminate线程结束前调用的方法指针

2.1线程的创建、运行和终止

线程类调用继承自父类的构造器 (con-structor Create) 创建对象实例, 接着调用线程管理例程的Addthread全局例程将全局线程记数值加1, 随后即通过线程管理例程中的BeginThread全局例程调用Windows API函数Createthread, 以参数形式向其传入线程运行主函数Threadproc。在此之前还将全局变量IsMultiThread置为真, 以用于各线程安全地访问内存管理器实现多线程保护。值得注意的是, 线程类在创建内核线程对象时总是将其置为挂起状态 (为保证线程对象初始化安全) , 而在其After Construction (重载其父类的方法) 中, 即在线程类的对象创建后, 在判断CreateOnsuspend的保护变量后, 调用Resume方法使线程开始运行。其次, 线程运行主函数Threadproc代表了线程执行的全过程。当其开始运行时, 直接调用TThread的Execute虚方法 (线程的主要执行部分) , 线程执行完毕后, 置线程结束标记。执行Do Terminate方法运行Call Onterminate调用用户事件处理挂钩Onterminte处理用户线程结束前的清理工作。若选择FreeOnTermi-nate属性为真, 则线程运行完成后, 将通过Free方法调用析构器Destory释放线程对象, 关闭线程句柄, 将全局线程记数减1, 最后通过线程管理例程的EndThread调用API函数Exit Thread通知操作系统线程结束。

2.2线程的状态切换操作

Delphi中使用私有方法Get Priority和Set Priority获取和设置线程优先级 (封装Windows API的Get ThreadPriority和Set-ThreadPriority) 。但动态改变线程的优先级对线程的执行有潜在的危险, 不慎重地提高优先级可能会造成处理器资源分配的不合理, 使某些线程始终得不到处理器时间。最好是在线程构造器中创建线程对象后初始化正确的线程优先级。当用户需要挂起运行线程时, 通过公有方法Suspend设置线程挂起标记, 调用API函数SuspendThread挂起执行线程。唤醒线程时, 使用封装了API函数Re-sume Thread的公有Resume方法实现。运行的线程是可以多次挂起和重新执行的。

2.3线程间的同步

多个线程在同一进程空间内共享进程资源带来方便的同时, 也不可避免的带来各线程间的同步问题。在Delphi环境内, 有些对象对于多线程应用不一定是安全的, 其中, 带Session部件的数据库组件、图形对象 (如Tfont、Tpen、Canvas等) 、对象的构造和析构本身、分配内存等是线程安全的。而可视组件库VCL中有关图形设备接口GDI的属性和方法不是线程安全的。

三、结束语

异步多线程 第6篇

关键词:进程,线程,Java的多线程

1 进程与线程

操作系统中进程是指特定的代码序列在指定数据集合上的一次执行活动,是指并行程序的一次执行过程,在Windows95中,就是一个EXE文件的执行过程。是一个动态概念,具有动态属性,每一个进程都是由内核对象和地址空间所组成的,内核对象可以让系统在其内存放有关进程的统计信息并使系统能够以此来管理进程,而地址空间则包括了所有程序模块的代码和数据以及线程堆栈、堆分配空间等动态分配的空间。

通俗点讲,进程就是正在计算机上运行的可执行文件针对特定的输入数据的一个实例,从此意义上讲,进程应包含三部分内容,即:进程=PCB+程序段+数据(PCB:进程控制块),同一个可执行程序文件如果操作不同的输入数据就是两个不同的进程。

进程理解为一个线程容器,线程不能独立存在,它必须隶属于某个进程,而进程也至少拥有一个线程,如果一个进程的所有线程都结束了那么进程也就结束了。

所谓线程是指由进程进一步派生出来的一组代码(指令组)的执行过程。一个进程可以产生多个线程,这些线程都共享该进程的地址空间,它们可以并行、异步执行。在80年代后的现代操作系统中引入的线程,使之仅做为调度和分派的基本单位,但不拥有资源(仅拥有一点其运行中必不可少的资源:程序计数器、一组寄存器、栈),以便轻装运行,又不频繁对之调度、切换。采用线程最主要的好处是:使同一个程序能有几个并行执行的路径,提高并行执行程度,从而进一步提高执行速度;线程需要的系统开销比进程要小。应该说明的是,在Windows95中,“多任务”是基于线程而不是基于进程。

线程是允许进行并行计算的一个抽象概念,在另一个线程完成计算任务的同时,一个线程可以对图像进行更新,二个线程可以同时处理同一个进程发出的二个网络请求。

线程是进程调度的基本单位,负责执行在进程地址空间内的代码,线程是进程的一条执行路径,它包含独立的堆栈和CP U寄存器状态,每个线程共享所有的进程资源,包括打开的文件、信号标识及动态分配的内存等等。由此可见,传统的进程可称之为重型进程,线程可称之为轻型进程。

进程有三种基本状态:就绪、阻塞、执行。线程同样也有同样三种基本状态。在现代操作系统中,进程是分配单元,而线程是执行单元(如图1)。

2 进程与JAVA线程的区别

应用程序在执行过程中存在一个内存空间的初始入口点地址、一个程序执行过程中的代码执行序列以及用于标识进程结束的内存出口点地址,在进程执行过程中的每一时间点均有唯一的处理器指令与内存单元地址相对应。

Jav a语言中定义的线程(Th read)同样包括一个内存入口点地址、一个出口点地址以及能够顺序执行的代码序列。但是进程与线程的重要区别在于线程不能够单独执行,它必须运行在处于活动状态的应用程序进程中,因此可以定义线程是程序内部的具有并发性的顺序代码流。

Unix操作系统和Microsoft Windows操作系统支持多用户、多进程的并发执行,而Java语言支持应用程序进程内部的多个执行线程的并发执行。多线程的意义在于一个应用程序的多个逻辑单元可以并发地执行。但是多线程并不意味着多个用户进程在执行,操作系统也不把每个线程作为独立的进程来分配独立的系统资源。进程可以创建其子进程,子进程与父进程拥有不同的可执行代码和数据内存空间。而在用于代表应用程序的进程中多个线程共享数据内存空间,但保持每个线程拥有独立的执行堆栈和程序执行上下文(Context)。

基于上述区别,线程也可以称为轻型进程(Light Weight Process,LWP)。不同线程间允许任务协作和数据交换,使得在计算机系统资源消耗等方面非常廉价。

线程需要操作系统的支持,不是所有类型的计算机都支持多线程应用程序。Jav a程序设计语言将线程支持与语言运行环境结合在一起,提供了多任务并发执行的能力。这就好比一个人在处理家务的过程中,将衣服放到洗衣机中自动洗涤后将大米放在电饭锅里,然后开始做菜。等菜做好了,饭熟了同时衣服也洗好了。

需要注意的是,在应用程序中使用多线程不会增加CPU的数据处理能力。只有在多CPU的计算机或者在网络计算体系结构下,将Java程序划分为多个并发执行线程后,同时启动多个线程运行,使不同的线程运行在基于不同处理器的Java虚拟机中,才能提高应用程序的执行效率。

3 进程与多线程

多线程是指同时存在几个执行体,按几条不同的执行线索共同工作的情况。实现单个进程中的并发计算。各线程间共享进程空间的数据,并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。多线程的程序能更好地表述和解决现实世界的具体问题,是计算机应用开发和程序设计的一个必然发展趋势(如图2)。

4 Java与多线程

Jav a语言的一个重要功能特点就是内置对多线程的支持,它使得编程人员可以很方便地开发出具有多线程功能,能同时处理多个任务的功能强大的应用程序。Jav a的所有类都是在多线程的思想下定义的,Java利用线程使整个系统成为异步。每个Ja va程序都有一个隐含的主线程:

applicatio n main方法

Applet小程序,主线程指挥浏览器加载并执行java小程序。

4.1 Thread类在Java程序中的应用

Th re ad类综合了Ja v a程序中一个线程需要拥有的属性和方法,当生成一个Threa d类的对象后,一个新的线程诞生了。每个线程都是通过目标对象的方法run()来完成其操作的。方法run()称为线程体。提供线程体的目标对象是在初始化一个线程时指明的。

任何实现了Runnable接口(实现run()方法)的类实例都可以作为线程的目标对象。如:

线程在执行过程中存在下面状态转换:(如图3)

4.2 线程的创建和运行

Java在Java.lang.Thread和java.lang.Runnable类中提供了大部分的线程功能。创建一个线程非常简单,就是扩展Thread类,并调用start()。通过创建一个执行Runnable()的类,并将该类作为参数传递给Th re ad(),也可以定义一个线程。下面是一个简单的Java程序实例,其中有2个线程同时在从1数到5,并将结果打印出来。

4.3 线程的使用

Jav a中存在许多编程人员希望能够对线程使用的标准操作:例如,测试线程是否存在、加入一个线程直到它死亡、杀死一个线程等。

结语

从传统操作系统中的进程到现代操作系统中的线程,再到多线程的核心目标是更好的提高多道环境下程序并行执行的程度,从而更好的提高系统吞吐量和系统处理能力。当然在一定程度上同时也增加了系统的时空开销。

参考文献

[1]汤子赢等著,《计算机操作系统》,西安:西安电子科技大学出版社,1999.390~414.

[2][美]Robin Rark著,前导工作室译,《UNIX技术大全》.北京:机械工业出版社,1998.

[3]孟庆昌等著《UNIX教程》(修订本)西安:电子工业出版社出版.2000.141~175.

Python多线程机制初探 第7篇

当我们上网观看视频时,我们不希望等很长时间当视频全部下载完成再观看,又比如当开发需要支持大量客户机的服务器的应用程序时,我们希望服务器“并发地”连接许多客户端,从而缩短用户(客户端)响应时间,这个时候使用线程就是十分必要的了。实际上象迅雷、网际快车,在线视频播放工具等已经在自己软件中实现了“线程”、“并发”。

如何使用线程?Python标准库中有两个模块_thread和threading来提供对线程的支持。其中_thread模块使用较初级的方式使用和控制线程,但它有较大的编程灵活性。而threading模块通过对thread进行二次封装,提供了更方便的应用程序接口来处理线程。

下面让我们看看_thread和threading是怎么进行多线程编程的。

2_thread模块中的线程:

关于_thread模块中线程的运用,先看一个例子:

这里介绍一下关键代码:

(1)import_thread是导入_thread线程模块以便使用python的thread功能;

(2)lock=_thread.allocate_lock()是创建一个锁,以便保证线程之间的正常通信,从而保证计算的正确性;

(3)def threadAdd()函数定义了一个实现count自增1的主函数;

(4)lock.release()语句释放锁以便其它线程可以使用本线程的数据;

(5)for i in range(10):

_thread.start_new_thread(threadAdd,())

循环语句启动10个线程,每个线程进行一次count自增1的计算。

这里要注意lock=_thread.allocate_lock()、lock.acquire()和lock.release()对线程锁的创建获取和释放是必要的,否则会导致错误的计算结果。

再次重复一下,_thread模块中lock()、.acquire()、.release()、start()是几个重要的方法和属性。

3 threading模块中的线程

threading模块,还是上面哪个例子,如何用threading实现:

这个例子中的关键代码:

(1)import threading是导入threading线程模块;

(2)class Add(threading.Thread)是一个能够实现用户功能的用户自定义类,由用户创建,初始化def__init__(self,lock,threadName)函数接收lock锁和threadName线程名两个参数。类class Add(threading.Thread)则重写了父类的run方法;

(3)for i in range(10):

Add(lock,"thread-"+str(i)).start()

循环启动十个线程,同时进行自定义类class Add(threading.Thread)中的用户自定义计算函数;

treading.Thread类还定义了以下几个重要函数:

join()等待某线程结束后再运行

getName()获得线程对象名称

setName()设置获得线程对象名称

isAlive()返回线程是否是活动的

需要指出的是,线程并不能解决所有的问题,有时我们使用进程更合适,进程与线程是相对的,进程相比线程它应该是更高一个数量级的并行处理手段,有关进程的资料,请参考官方文档。

以上代码在Python3.1+Windows XP环境下运行通过。

参考文献

[1]Python v3.1.2 documentation.

[2]Python Threading-Basics.

[3]Multi-threaded proxy scrapper.

异步多线程范文

异步多线程范文(精选7篇)异步多线程 第1篇远程抄表系统近年来得到日益广泛的应用,物业管理公司可以用它来节省人工抄表的人力,建筑节能系...
点击下载文档文档内容为doc格式

声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。如若本站内容侵犯了原著者的合法权益,可联系本站删除。

确认删除?
回到顶部