多线程程序范文
多线程程序范文(精选11篇)
多线程程序 第1篇
关键词:多线程,多处理器,进程,函数
引言
Windows操作系统下的多线程编程方式由来已久,早期的多处理器系统价格昂贵,加之多线程编程时相对复杂,因此应用不广泛。自从Intel和AMD公司相继推出单芯片多核处理器以来,多处理器系统身价骤降,现在以Core2Duo为代表的多处理器系统已经成为当前桌面计算机的主流配置,工控机领域内也有大量的成熟产品面世,从硬件条件上为多线程在测控领域的广泛应用打下了牢固的硬件基础,本文就从这两个方面分别进行论述。
1 基本概念
1.1 处理器系统
处理器系统可分为两种(单处理器和多处理器),仅具有一个中央处理单元(CPU)的计算机系统称为单处理器系统,具有两个或两个以上中央处理单元的计算机系统称为多处理器系统。在当前常见的多处理器系统中,多个CPU有以下几种构成方式:
1)多个物理处理器,从硬件形态上看就是主板上包含多个CPU芯片,这些芯片之间通过专用的超高速总线互联;
2)单个处理器芯片中集成多个处理器核心,每个核中均有独立的处理器逻辑单元,处理器核心间的调度和互联控制也集成在芯片上的,因此本质上相当于多个独立的物理处理器;
3)支持超线程技术的单个处理器,在一个物理处理器上提供多个逻辑处理器,每个逻辑处理器有自己的状态寄存器,但执行引擎和高速缓存是共享的。
1.2 进程和线程
程序、进程和线程都是操作系统的概念,程序是可执行代码和资源的组合,进程是一个正在运行的程序的实例;线程是进程内部的一个执行单元,系统创建好进程后,实际上就启动执行了该进程的主执行线程。在Windows系统中,程序的执行是通过进程进行组织和管理的,实质上程序仅仅是代码的集合,它仅包括程序执行代码和必须的资源,而进程则相当于一个容器,它包含了执行一个程序所需要的必要资源,进程必须包括至少一个线程,后面的内容会更详细的说明有关线程的内容。
1.3 多线程
启动程序过程其实就是创建进程的过程,从本质上来说,进程仅是操作系统为执行程序所建立的资源集合,进程本身不执行任何程序代码,真正执行代码的就是线程,可以简单的把线程理解为正在运行的代码,在一个确定的时刻,线程就像是某个具体的函数正在执行。
一个进程中所包含的线程数量超过一个时,就被称之为“多线程”,在当前的Windows系统中用自带的任务管理器就可以查看当前系统中运行的所有进程和其中的线程数。
2 多线程创建方法
Windows是个多任务操作系统,它可以同时创建多个进程,也支持多线程,这两种方式都能够提高系统的利用率,举例来说,我们打开两个文本文件进行交互编辑,如果采用“记事本”程序,则每个打开的文档都是一个独立的进程;如果采用“Word”进行编辑,则“WinWord”进程只有一个,随着打开文档的增多,线程数会增加,其情景可参见图1中系统截屏所示。
从操作系统角度分析,创建进程所需要的资源远大于创建线程所需的资源;另外,多线程的进程中,每个线程都可以访问本进程的内存地址空间,共享DLL映像,如果可能,采用多线程的编程方式会更有效率,但这并非硬性的原则,具体应用还需要具体分析。
开发应用程序时,Windows提供了专门用于创建线程的的API函数,即CreateThread调用,由于使用该API调用需要设置安全属性、配置线程堆栈等,稍显复杂,在实际开发应用程序时最好使用集成开发环境提供的线程操作函数,如采用VC++的MFC进行开发时,可以使用AfxBeginThread函数,本文根据MFC的开发方式进行描述,相关的函数和编码示例均以MFC应用程序风格展示。
AfxBeginThread是重载函数,MSDN中提供了两种函数原型,分别是:
CWinThread*AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam,int nPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);和CWinThread*AfxBeginThread(CRuntimeClass*pThreadClass,int nPriority=THREAD_PRIORITY_N O R M A L,U I N T n S t a c k S i z e=0,DWORD dwCreateFlags=0,L P S E C U R I T Y_A T T R I B U T E SlpSecurityAttrs=NULL);
两个函数只有最初的参数不同,第一个函数首参数是线程入口指针;第二个参数一个参数指针,该指针的内容可以由编程人员任意设定;第三个参数是用于设置线程调度优先级的,默认值是普通优先级;第四个参数是用于设置线程堆栈长度的,一般情况下采用默认参数即可;第五个参数是一个附件标记,默认值指定线程一旦创建成功则立即提交系统调度执行,如果设置为CREATE_SUSPENDED(宏定义,值为4)则线程创建完毕后就被挂起,直到调用ResumeThread才能够执行;最后一个参数是用于设置安全属性的。
第二个函数的首参数是一个CWinThread类的对象指针,后面的参数与第一个函数含义相同。能够使用动态创建等MFC的特性,可以实现比较复杂的封装。以上两个函数的返回值均是CWinThread对象指针。
更常用的方式是第一种AfxBeginThread函数调用,即提供一个线程函数入口,该函数用于实现线程的主要工作任务,函数原型如下:
UINT MyControllingFunction(LPVOID pParam);
参数是一个指针,未确定具体类型,该指针值就是AfxBeginThread中的pParam参数值。函数的返回值则是线程的退出码。如果开发者将线程函数定义成类的成员函数,则该函数必须定义成Static类型,否则会出现编译错误。
3 应用实例
在多处理器系统中,测控程序合理运用多线程技术,可以花费较少费用解决一些以往难以解决的问题,现举一个实例进行说明。
某型号产品测试设备需要完成对被测产品的控制指令下发,按照技术要求,必须每隔1.25毫秒通过RS422通讯接口发送一帧控制指令,采用多处理器系统,可以设计一个专门的计时线程,用无限循环方式获取系统提供的性能计数,从而计算时间间隔,以纯软件方法实现任意时间间隔,实现方法简要说明如下:
首先建立一个基于D a i l o g的M F C应用程序项目,其中的用户界面控制类CComm422TDlg除了实现与用户之间的交互控制外,还负责在测试工作开始后创建指令发送线程,创建线程和结束线程的操作由成员函数OnOpenclose()实现,主要代码如下:
在以上应用中,如果将该控制程序运行在一个单CPU的系统上,则会导致整个系统反应迟滞,连移动鼠标都不流畅,而在多CPU系统中,则一切操作都很正常,如果通过示波器监测通讯接口的状态,可以发现指令发送间隔稳定在预期值,完全达到设计要求。
在多线程编程中,应该注意线程间的同步问题,特别是当多个线程都对某个变量或某段内存进行数据读写时,如果同步失当,可能会导致灾难性的后果。在某些应用中需要用到同步操作时,可以使用事件对象配合等待函数WaitForSingleObject,或者相应的Windows内核对象,如临界区对象、互斥体对象等来进行同步操作。
4 结语
多处理系统的日益普及,带来多线程技术在测试程序中广泛应用,通过多线程技术的应用,大大提升了处理器的利用效率,降低成本。
参考文献
[1]Rand E Bryant(美).深入理解计算机系统(修订版).中国电力出版社,2007-04
[2]Jeffrey,J(美).Windows核心编程.清华大学出版社,2008-09
[3]潘爱民.Windows内核原理与实现.电子工业出版社,2010-05
[4]侯捷.Win32多线程程序设计.华中科技大学出版社,2002-04
Java程序员面试中的多线程问题 第2篇
(Collections Framework),理解核心线程概念时,娴熟的实际经验是必需的。这篇文章收集了Java线程方面一些典型的问题,这些问题经常被高级工程师所问到。
0、Java中多线程同步是什么?
在多线程程序下,同步能控制对共享资源的访问。如果没有同步,当一个Java线程在修改一个共享变量时,另外一个线程正在使用或者更新同一个变量,这样容易导致程序出现错误的结果。
1、解释实现多线程的几种方法?
一Java线程可以实现Runnable接口或者继承Thread类来实现,当你打算多重继承时,优先选择实现Runnable。
2、Thread.start()与Thread.run()有什么区别?
Thread.start()方法(native)启动线程,使之进入就绪状态,当cpu分配时间该线程时,由JVM调度执行run()方法。
3、为什么需要run()和start()方法,我们可以只用run()方法来完成任务吗? 我们需要run()&start()这两个方法是因为JVM创建一个单独的线程不同于普通方法的调用,所以这项工作由线程的start方法来完成,start由本地方法实现,需要显示地被调用,使用这俩个方法的另外一个好处是任何一个对象都可以作为线程运行,只要实现了Runnable接口,这就避免因继承了Thread类而造成的Java的多继承问题。
4、什么是ThreadLocal类,怎么使用它?
ThreadLocal是一个线程级别的局部变量,并非“本地线程”。ThreadLocal为每个使用该变量的线程提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本(译者注)。
下面是线程局部变量(ThreadLocal variables)的关键点:
一个线程局部变量(ThreadLocal variables)为每个线程方便地提供了一个单独的变量。
ThreadLocal实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。
当多个线程访问ThreadLocal实例时,每个线程维护ThreadLocal提供的独立的变量副本。
常用的使用可在DAO模式中见到,当DAO类作为一个单例类时,数据库链接(connection)被每一个线程独立的维护,互不影响。(基于线程的单例)ThreadLocal难于理解,下面这些引用连接有助于你更好的理解它。《Good article on ThreadLocal on IBM DeveloperWorks 》、《理解
ThreadLocal》、《Managing data : Good example》、《Refer Java API Docs》
6、Sleep()、suspend()和wait()之间有什么区别?
Thread.sleep()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了interrupt()方法,它将唤醒那个“睡眠的”线程。
注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep(),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep(),也是当前线程进入睡眠,而不是t线程。t.suspend()是过时的方法,使用suspend()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend()容易引起死锁问题。
object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而不是thread。调用object.wait()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用object.notify(),这样将唤醒原来等待中的线程,然后释放该锁。基本上wait()/notify()与sleep()/interrupt()类似,只是前者需要获取对象锁。
7、在静态方法上使用同步时会发生什么事?
同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。
8、当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗? 可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据Java照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。
9、在一个对象上两个线程可以调用两个不同的同步实例方法吗?
不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其它同步方法。
看下面代码示例非常清晰:Common 类 有synchronizedMethod1()和synchronizedMethod2()方法,MyThread调用这两个方法。
10、什么是死锁
死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就JavaAPI而言,线程死锁可能发生在一下情况。
● 当两个线程相互调用Thread.join()
● 当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。
11、什么是线程饿死,什么是活锁?
线程饿死和活锁虽然不想是死锁一样的常见问题,但是对于并发编程的设计者来说就像一次邂逅一样。
当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI中线程活锁可能发生在以下情形:
● 当所有线程在程序中执行Object.wait(0),参数为0的wait方法。程序将发生活锁直到在相应的对象上有线程调用Object.notify()或者
Object.notifyAll()。
● 当所有线程卡在无限循环中。
多线程程序 第3篇
1 工作原理
当发送端需要发送数据时,首先输入接收端的IP地址以及发送端使用的昵称,然后输入数据并单击发送。这样,数据便被写入发送端的SOCKET并且发送出去。当接收端检测到有数据到达时,就会自动调用线程函数从SOCKET中读出数据并显示给用户。这就是聊天工具的通信原理。其实现主要使用以下两种技术:
1)SOCKET:网络套接字,它在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有SOCKET接口的计算机通信。应用程序在网络上发送、接收的信息都通过这个SOCKET接口来实现。基于SOCKET的局域网通信是一种灵活的、易于实现的、低成本的方法。它可以运行在各种使用TCP/IP协议作为通讯协议的网络上。在应用开发中就像使用文件句柄一样,可以对SOCKET进行读,写操作。考虑到局域网网络质量较好,丢包率低,本系统采用基于UDP的数据报套接字(SOCK_DGRAM),它具有资源消耗小,处理速度快的优点。我们聊天用的ICQ和QQ就是基于UDP协议实现的。基于UDP的SOCKET编程模型如图1所示。
2)多线程:多线程是指一个程序中可以同时运行多个不同的线程来执行不同的任务。它通过提高资源利用效率来提高系统的效率。每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。
用户的聊天过程其实就是发送和接收数据的过程,在程序设计方面也有多种处理方式。本程序中,在接收数据时,我们调用函数recvform()来实现,然而,默认情况下SOCKET工作在阻塞模式,如果在没有数据的情况下调用recvform()函数,则主线程将会被挂起,直到有数据为止。所以,我们就需要单独创建一个线程来接收数据。从而使发送和接收两个操作同步执行。
2 程序实现
建立一个基于对话框的MFC应用程序工程,名称为Chat。本程序是集服务器和客户端为一体的,在编写程序时,笔者将它分为接收端和发送端两部分来实现。程序运行如图2所示。
2.1 接收端
在接收端,首先连接套接字库,建立SOCKET。然后创建一个线程函数来接收数据。
2.2 发送端
发送端实现起来相对容易,因为SOCKET已经建立,只需要输入接收端的IP地址以及发送端使用的昵称,单击发送按钮便完成了发送操作。
3 结束语
本文详细介绍了局域网环境下聊天程序的开发过程,重点分析了基于UDP的SOCKET编程模型和多线程的综合应用。此外,程序只实现了聊天程序的主体功能,读者可以在此基础上根据自己的需要开发具体的应用程序,比如说聊天室程序,类似QQ的聊天工具等等。
摘要:在网络越来越发达的今天,聊天已经成了许多人生活中必不可少的一部分。基于这样的需求,该文使用VC++6.0作为开发平台,以Windows SOCKET为基础,采用多线程技术实现一个局域网聊天工具。
关键词:VC,SOCKET,多线程,UDP
参考文献
[1]蔡毓.UDP的Java聊天程序[J].电脑编程技巧与维护,2009(7).
[2]刘洪海.基于局域网的通讯的研究与实现[J].硅谷,2010(5).
Java线程及多线程技术及应用 第4篇
用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable)。
2、就绪状态
处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列,等待系统为其分配CPU。等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
3、死亡状态
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:
一个是正常运行的线程完成了它的全部工作;
另一个是线程被强制性地终止,如通过执行stop或destroy方法来终止一个线程。
Method stop & destroy() in the class Thread is deprecated。
当一个线程进入死亡状态以后,就不能再回到其它状态了。 让一个Thread对象重新执行一次的唯一方法,就是重新产生一个Thread对象。
4、体现线程状态转变的代码示例
package com.px1987.j2se.thread.base;
public class MyRunable1 implements Runnable {
public void run() {
while (true)
System.out.println(invoke MyRunable run method);
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunable()); // 新生状态
thread.start(); // 就绪状态,获得CPU后就能运行
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.stop(); // 死亡状态
}
}
通过查API可以看到stop方法和destory方法已经过时了,所以不能再用,那要怎样做才能强制的销毁一个线程呢?
1、在run方法中执行return 线程同样结束
2、可以在while循环的条件中设定一个标志位,当它等于false的时候,while循环就不在运行,这样线程也就结束了。代码为实现的代码示例:
package com.px1987.j2se.thread.StateControl;
public class MyRunable2 implements Runnable {
private boolean isStop; //线程是否停止的标志位
public void run() {
while (!isStop)
System.out.println(invoke MyRunable run method);
}
public void stop(){ //终止线程
isStop=true;
}
public static void main(String[] args) {
MyRunable myRunable=new MyRunable();
Thread thread = new Thread(myRunable);
thread.start();
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
myRunable.stop(); //正确的停止线程的方法
}
}
5、阻塞状态
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。
在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。有三种方法可以暂停Threads执行:
(1)sleep方法
可以调用Thread的静态方法:public static void sleep(long millis) throws InterruptedException 使得当前线程休眠(暂时停止执行millis毫秒)。由于是静态方法,sleep可以由类名直接调用:Thread.sleep()。下面为代码示例:
package com.px1987.j2se.thread.p5;
import java.util.Date;
import java.text.SimpleDateFormat;
class SleepTest implements Runnable {
private static SimpleDateFormat format = new SimpleDateFormat(yyyy-MM-dd hh:mm:ss);
public void run() {
System.out.println(child thread begin);
int i = 0;
while (i++ < 5) {
System.out.println(format.format(new Date()));
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(child thread dead at: + format.format(new Date()));
}
public static void main(String[] args) {
Runnable r = new SleepTest();
Thread thread = new Thread(r);
thread.start();
try {
Thread.sleep(0);
}
catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println(main method dead!);
}
}
该程序的运行结果如下:
child thread begin
-02-06 04:50:29
2009-02-06 04:50:34
2009-02-06 04:50:39
2009-02-06 04:50:44
main method dead!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.px1987.j2se.thread.p5.Thread4.run(Thread4.java:17)
at java.lang.Thread.run(Unknown Source)
2009-02-06 04:50:49
child thread dead at: 2009-02-06 04:50:54
(2)yield方法
让出CPU的使用权,从运行态直接进入就绪态。下面为代码示例:
package com.px1987.j2se.thread.StateControl;
class Thread5 implements Runnable {
private String name;
Thread5(String s) {
this.name = s;
}
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println(name + : + i);
if (i % 10 == 0) {
Thread.yield();
}
}
}
}
package com.px1987.j2se.thread.StateControl;
public class YieldTest {
public static void main(String[] args) {
Runnable r1 = new Thread5(S1);
Runnable r2 = new Thread5(S2);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
try {
Thread.sleep(2);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(main method over!);
}
}
该程序的部分运行结果如下:
S1: 20
S2: 7
S2: 8
S2: 9
S2: 10
S1: 41
S1: 42
S1: 43
S1: 44
S1: 45
S1: 46
S1: 47
S1: 48
S1: 49
S1: 50
S2: 11
S2: 12
(3)join方法
当某个(A)线程等待另一个线程(B)执行结束后,才继续执行时,使用join方法。A的 run方法调用b.join()。下面为代码示例。
package com.px1987.j2se.thread.join;
class FatherThread implements Runnable {
public void run() {
System.out.println(爸爸想抽烟,发现烟抽完了);
System.out.println(爸爸让儿子去买包红塔山);
Thread son = new Thread(new SonThread());
son.start();
System.out.println(爸爸等儿子买烟回来);
try { //join含义:等待son线程执行完毕,father线程才继续执行
son.join();
}
catch (InterruptedException e) {
System.out.println(爸爸出门去找儿子跑哪去了);
System.exit(1);
}
System.out.println(爸爸高兴的接过烟开始抽,并把零钱给了儿子);
}
}
package com.px1987.j2se.thread.join;
class SonThread implements Runnable {
public void run() {
String tabs= ;
System.out.println(tabs+儿子出门去买烟);
System.out.println(tabs+儿子买烟需要10分钟);
try {
for (int i = 0; i < 10;) {
Thread.sleep(1000);
System.out.println(tabs+儿子出去第 + ++i + 分钟);
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tabs+儿子买烟回来了);
}
}
package com.px1987.j2se.thread.join;
public class JoinTest {
public static void main(String[] args) {
System.out.println(爸爸和儿子的故事);
Thread father = new Thread(new FatherThread());
father.start();
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// father.interrupt();
}
}
该程序的运行结果如下:
爸爸和儿子的故事
爸爸想抽烟,发现烟抽完了
爸爸让儿子去买包红塔山
爸爸等儿子买烟回来
儿子出门去买烟
儿子买烟需要10分钟
儿子出去第1分钟
儿子出去第2分钟
儿子出去第3分钟
儿子出去第4分钟
儿子出去第5分钟
儿子出去第6分钟
儿子出去第7分钟
儿子出去第8分钟
儿子出去第9分钟
儿子出去第10分钟
儿子买烟回来了
爸爸高兴的接过烟开始抽,并把零钱给了儿子
当时间来到儿子出去买烟的时候,Father线程调用interrupt方法就会打断son线程的正常执行,从而father线程也就不必等待son线程执行完毕再行动了,运行结果如下:
爸爸和儿子的故事
爸爸想抽烟,发现烟抽完了
爸爸让儿子去买包红塔山
爸爸等儿子买烟回来
儿子出门去买烟
儿子买烟需要10分钟
儿子出去第1分钟
儿子出去第2分钟
儿子出去第3分钟
儿子出去第4分钟
多线程技术在VB.NET中的实现 第5篇
关键词:进程;多线程;.NET框架;VB.NET
中图分类号:TP311.1
应用程序开发过程中处理并发问题以及多任务管理的问题经常使用多线程编程技术这一程序设计中广泛应用的技术,这一技术是这类问题最简便的解决方案。当应用程序需要多个操作同时运行时,多线程技术可以一个线程在接收键盘输入数据的同时另一个线程计算并进行数据传输,而其它的线程可以同时完成屏幕的刷新显示或从外部设备读入数据等任务。VB.NET通过CLR(Common Language Runtime)提供了对多线程机制的支持。
1 多线程技术的相关概念及优势
1.1 关于进程、线程和多线程概念的理解
进程指的是应用程序的一个具体运行实例,可以认为是程序处于某一次动态执行状态。我们认为操作系统中拥有系统资源的基本单位和独立调度、分派任务的基本单位是进程。线程则是进程内部的一个执行单元或异步代码路径,每个进程可以包含若干个线程。线程是比进程更小的独立运行的基本单位,引入线程的目的是为了减少程序并发执行时的所付出的开销,使操作系统具有更好的并发性。线程只占用一些运行中必不可少的资源(程序计数器、一些寄存器和栈),除此之外不占用其它资源,但是同一进程中的线程可与其他线程共享分配给进程的系统资源,例如分配给进程的虚拟内存空间和其它的系统资源,且同一进程中的线程可以并发执行,线程的并发执行实质上是多个线程以轮流占用时间片的方式共享处理器时间。如果一个应用程序同时包含多个线程时,则称该应用程序使用了多线程技术。
1.2 多线程机制在软件开发中的优势
多线程机制的主要优势表现在提高了处理器处理时间的利用率,能够以更快的速度对用户的需求进行响应,从而提高了应用程序中进程的整体执行效率,并且增强了应用程序的适应性。多线程技术处理一个交互应用程序的时候,当线程的一部分被阻塞的时候,该应用程序还能继续运行,因此对用户增强了响应能力。多线程技术可以让程序中占用大量处理时间的任务或当前没有进行处理的任务定期将处理时间让给别的任务;可以随时停止任务;可以设置每个任务的优先级以优化程序性能。
多线程技术在软件开发的广泛应用正是基于上述优势。例如,当程序需要进行费时的I/O操作时;分布式应用环境下,更多的用户可以通过线程方式分享服务器上的处理器资源以提升扩展性;OA应用程序也可以通过多线程中处理后台操作以提升处理效率。
2 VB.NET对多线程技术的支持
网络应用程序一般情况下均是多任务并发运行环境,要求很高的运行效率,而这正是多线程技术的优势所在。VB.NET基于.NET Framework,而NET Framework框架的重要组成部分CLR(通用语言运行时)实现了多线程机制,从而包含了对多线程技术的支持。可以使用System命名空间下的Threading类在程序中创建多个线程、对线程进行管理并且支持线程池等增强功能的实现。VB.NET,VC#.NET等.NET Framework框架下的语言编在开发多线程应用程序的过程中,均可以使用Threading类的方法和属性,不用像VB6.0那样再去非常麻烦的调用Win32 API函数,使得开发过程更为简化并且有效减少了各种错误的产生。
3 多线程编程在VB.NET中的实现
3.1 线程的创建和控制
VB.NET中线程的创建和控制主要通过.NET基础类库中System.命名空间的Thread类进行实现,Thread类用于创建线程并对线程进行控制操作,并可以获取和设置线程的优先级和当前状态。
对一个线程进行创建和控制操作的大致步骤是首先引入Thread类所属的命名空间:Imports System.Threading,接着创建一个Thread类的对象,并且通过AddressOf子句传送委托给需要调用的具体过程,然后启动运行线程。进行线程创建操作后,可以使用Thread类的Start方法启动线程运行,使用Suspend和Resume方法将线程挂起或将挂起的线程恢复运行,此外Thread类还提供了Interrupt、Sleep、Abort及Join等多种方法以控制操作线程。Thread类还具有Name、IsAlive、PriorityIsBackground和ThreadState等重要属性,通过这些属性可以获取或更改当前线程的状态。
3.2 线程生命周期中的状态转换及线程优先级设置
线程在整个生命周期中处于不同的状态,Thread类的ThreadState属性决定了线程的状态。初始创建线程时,线程处于Unstarted状态,使用Thread类的Start()方法可将线程状态转换为Running状态。Running状态下调用Suspend()方法将线程状态转换为Suspended状态,直到调用resume()方法使线程重新运行而处于Running状态。如果调用Thread.Abort()方法,线程将停止运行并处于处于Stopped状态。
线程的优先级指的是线程对处理器时间的优先占用权。通常情况下线程轮流占用处理器时间片,但当高优先级的线程与低优先级的线程并发执行时,操作系统优先将处理器时间片分配给高优先级的线程。通过Thread类的Priority属性可以设置线程所具有的优先级,Priority属性值为枚举类型ThreadPriority。
3.3 线程间的同步问题
线程同步问题是指多个线程之间相互占用对方资源导致各线程的任务无法继续执行的问题,例如多个线程同时访问同一对象,或者多个线程运行同一段程序代码,这些情况下各个线程均处于阻塞状态。
为了避免这一问题的产生,需要通过某种方法进行线程间的同步。一种同步方法是可以使用VB.NET中的Synclock语句块,将多个线程可能同时访问的对象或同时执行的程序代码段放入Synclock语句块,使的线程可以得到对象引用的独占锁,从而避免多个线程同时访问同一对象,或者多个线程运行同一段程序代码。在SyncLock语句中要得到某一对象引用的独占锁,一般通过调用GetType方法获取与所有类相关联的System.Type对象实现。另一种同步方法是调用Thread类的Join()方法使得调用该方法的线程处于指定时间的阻塞状态,直到其它的线程执行完毕,通过判断Join()方法的返回值可以判断出其它的线程在指定时间内是否已经执行完毕,返回值为True表明已经执行完毕,返回值为False表明尚未执行完毕,通过这种方式来实现线程同步。
4 结束语
多线程技术是提升应用程序执行效率,进一步增强程序并发性,充分利用系统资源的一种重要手段,也是目前编程技术的核心思想之一。使用VB.NET的多线程技术进行应用程序开发,可以有效的提高应用程序开发效率并且大大缩短程序响应时间,而且能够对系统资源进行更加有效的利用。
参考文献:
[1]程伟,肖文敏.Visual Basic.Net的多线程机制[J].电脑开发与应用,2007(11).
[2]张焰林.基于VB.NET的多线程技术应用[J].计算机系统应用,2009(02).
作者简介:陈俊伟(1976-),男,重庆铜梁人,教师,讲师,硕士,研究方向:软件技术,计算机网络。
多线程程序 第6篇
Java自问世以来,以其面向对象、跨平台、可移置性强、网络编程等独有魅力受到许多程序员青睐。为了实现基于Internet的多对一在线聊天系统,可选择用Java的多线程技术实现多任务处理,即多个客户端和服务器实现在线聊天;用基于Java的Socket技术实现网络用户之间的信息交流,即在线聊天。
2 Socket通信技术
2.1 概念
Socket即套接字,是TCP/IP协议的通信端口,通过它实现客户机/服务器的网络通信。套接字是Internet通信的端点,它封装了连接功能,使得网络通信变得简单,通过创建通信两端的Socket,即常说的“插座”,建立客户机与服务器两端通信的通道,两端进程通过该通道实现数据通信。
2.2 Socket通信机制
Socket实现网络通信的流程如图1所示。
(1)服务器端通过ServerSocket类创建服务器套接字,指定某一端口监听来自客户机的请求,客户机通过Socket类创建客户套接字toserver,向服务器端套接字ServerSocket发出呼叫请求。
(2)一旦服务器套接字监听到有来自客户机的请求,马上创建一个新的Socket套接字fromclient,建立起与客户机的连接,基于此连接实现客户机与服务器的数据通信。
(3)客户机与服务器的数据通信通道建立好之后,通过输入、输出流对象分别读出来自对象的数据以及写入即将发送到对方的数据,实现了两端的信息交换。
(4)通信结束后,关闭服务器端与客户端通信的fromclient套接字对象,关闭客户端toserver套接字对象,关闭服务器端serversocket对象,以及所有输入流、输出流对象。
3 Socket常用的API
Java.NET包中封装了服务器套接字ServerSocket以及客户机套接字Socket。ServerSocket类是一个用于监听客户请求的Internet服务器程序的类,但它实际上不执行数据通信工作,它仅仅用于监听和接受客户请求,然后为客户创建一个相应的Socket对象。Socket是支持TCP协议的基本类,利用Socket类提供的一些输入和输出方法从套接字中读取及写入数据。实际上双方的通信是通过Socket对象实现的。SocketServer及Socket类常用的API简介如下:
(1)java.net.SocketServer包
1)ServerSocket(int port)用于创建在指定的port端口监听的服务器套接字ServerSocket。
2)public Socket accept()用于监听来自客户机的请求,一旦有来自客户机的请求,随即返回一个与客户机Socket相对应的Socket。
3)public void close()用于关闭服务器套接字ServerSocket。
(2)java.net.Socket包
1)Socket(String ipAddress,int port)用于创建向指定端口号port和指定IP地址的服务器发送通信请求。
2)public InetAddress getInetAddress()用于返回套接字所连接的远程主机地址。
3)public InputStream getInputStream()用于返回该套接字的输入流,读出该套接字信息。
4)public OutputStream getOutpputStream()用于返回该套接字的输出流,将要发送的信息写入该套接字。
4 多线程处理
当服务器需要同时处理来自多个客户机的信息时,再采用单线程的方式是不能实现的,此时就要用到多线程技术,Java语言的一个重要特点就是支持多线程处理,使得开发多线程程序变得相当容易。通过Thread类创建线程对象,在run()方法中运行某线程要执行的语句。
5 服务器端的Socket实现
在基于Web的一对多在线聊天程序中,首先要启动服务器端程序,用以监听来自客户机的聊天请求,一旦监听到请求,返回相应的Socket对象后,二者之间的数据通信通道建立,可以实现聊天。聊天结束后,关闭此连接。具体过程如下:
(1)创建服务器套接字
ServerSocket server=new ServerSocket(2001,30)//创建//一服务器套接字,允许同时和30个客户进行聊天,通过端//口2001监听来自客户机的请求
(2)监听并实现连接
Socket fromclient=server.accept()//监听客户请求,一//旦有来自客户端的请求,随即返回相应的Socket套接字,建//立数据通信的通道。
(3)获取输入、输出流对象
(4)聊天信息的写入与读出
(5)关键代码
6 客户端的Socket实现
客户端应用程序与服务器端程序有很多相似之处,所不同的是只需要创建各个客户的Socket套接字,通过指定发送连接请求到指定的服务器,实现通信连接。关键代码如下:
7 结语
Java一大特色就是可以基于网络进行编程,以上仅仅初步探讨了用Socket实现网络聊天的简单应用,对于Socket在网络通信的应用还具有更多实用价值以服务于各行各业。
参考文献
[1]谷岩.利用Java的Socket编程机制实现在线交谈[J].计算机工程与设计,2004,(6):944-946.
[2]陈小燕.用Java实现多线程Socket通信[J].湖北广播电视大学学报,2008,(1):157-158.
[3]邹吕新,钟仲芳.基于Java的网络通信的设计与实现[J].电脑知识与技术,2010,(6):1858-1860.
多线程程序 第7篇
在多线程显示UI中普遍存在的问题是从当前线程的模块-线程状态的永久句柄表或临时句柄表中无法查找到另一个线程对应窗口句柄的MFC窗口类对象,而MFC类库和第三方界面库的界面更新逻辑对具有父子关系的窗口均假设为在同一线程进行,因此当从主框架开始进行消息传递来更新界面时,如果某一窗口句柄在当前线程的永久句柄表中查找不到MFC窗口对象时,更新消息不会传递给该窗口,从而造成界面的局部未被更新。
解决此问题的方法是将主线程的模块-线程状态中的句柄表拷贝到子线程的模块-线程状态中的句柄表中。下面以在子线程中弹出一个工具条为例子加以说明,在线程中弹出一个工具条的代码如下:
以上代码是未将主线程的线程模块状态中的句柄表拷贝到子线程的线程模块状态中的句柄表时弹出工具条失败的代码,在debug版本会有许多断言错误,在debug和release版本均无法弹出工具条的样例代码
多线程程序 第8篇
关键词:带抑止弧Petri网,多线程,模型,C#编程
引言。在单个程序里面同时运行多个线程来完成不同任务, 称为多线程。多线程主要目的是为了节约CPU时间, 提高CUP的利用率。但多线程会给程序带来更多难以发现的bug。Petri是一种功能强大的分布式系统的建模分析工具。在描述、模拟、分析系统的顺序、冲突、并发和同步等关系上有着其他模拟分析工具难以比拟的优势。
1 带抑止弧Petri的概念。
定义:一个五元组Σ= (S, T, F, I, M) 组成一个带抑止弧的Petri网, (S, T, F) 代表一个网, 其中M表示一个网的标识, I<S×T, 称作抑止弧集, I∩F=a, 即Ps∈S∧Pt∈T; (s, t) ∈F→ (s, t) |I, 对t∈T, 如果 (a) Ps∈S: (s, t) ∈F→M (s) E1, (b) Ps∈S; (s, t) ∈I→M (s) =0, 则表明t在标识M有发生权, 则记作M[t>。
2 应用实例。
"生产者/消费者"问题是线程同步控制的非常典型的例子, 本文的程序中Producer线程负责生产商品, Consumer线程负责在有商品的时候消费商品。PandC类的对象是生产商品线程和消费商品线程共同访问的商品数据。
程序如下:
这个程序没有对线程进行同步, 运行程序时不会出现问题, 但是多次运行程序后会陷入死机程序出现错误。这个错误发生的几率很小, 错误的原因也难以查找。但是依据上文的建模规则建立这个程序的Petri网模型, 依靠Petri网的理论就能够分析和定为错误。该程序的Petri网模型如图1, 当库所p8, p14, p15, p16同时都有标记时, Petri网中没有一个变迁是可以引发, 即表示当前状态M是一个死标识, end库所不获得标记, 说明程序不能正常结束。对应的多线程的执行过程是当消费最后一份可消费的商品后要把t.available置为false, 但在执行t.available=false语句之前, 消费商品线程被中断, 系统执行了商品生产线程, 生产若干商品后, 生产商品线程结束, 再调用消费商品线程并执t.available=false语句, 最终导致消费商品线程判断有商品可消费但是t.available=false, 消费商品线程陷入死循环, 程序不能正确结束。
这个时候导致程序出错的原因已经找到, 只需要对消费商品线程进行同步即可, 即使得消费商品线程在执行时可以独占资源。
3 结束语
本文通过抑止弧Petri网模型的运行找到了"生产-消费"程序中用普通方法难以发现的隐藏的错误。而且通过引入抑止弧Petri网的行为分析找到了引发这个错误的原因并提出了该进的方法。
参考文献
[1]吴哲辉.Petri网导论[M].北京:机械工业出版社.
多线程程序 第9篇
(一) 进程和线程的概念
1、进程的定义、属性和状态
进程是一个可并发执行的具有独立功能的程序关于某个数据集合的一次执行过程, 也是操作系统进程资源分配和保护的基本单位。
它的属性包括: (1) 结构性:进程包含了数据集合和运行于其上的程序; (2) 共享性:多个不同的进程可以共享相同的程序; (3) 动态性:有生命周期, 由创建而产生, 由调度而执行, 由撤销而消亡; (4) 独立性:每个进程都可以各自独立的速度在CPU上推进; (5) 并发性:即同步; (6) 制约性:即互斥。
进程具有三种不同的状态:就绪、运行、阻塞。
2、线程的定义、属性、状态以及线程的调度
线程是操作系统进程中能够独立执行的实体 (控制流) , 是处理器调度和分派的基本单位。线程是进程的组成部分, 每个进程内允许包含多个并发执行的线程。同一个进程中的所有线程共享进程获得的主存空间和资源, 但不拥有资源。
线程的属性包括: (1) 并发性:同一进程的多个线程可在一个/多个处理器上并发或并行地执行; (2) 共享性:同一个进程中的所有线程共享但不拥有进程的状态和资源, 且驻留在进程的同一个主存地址空间中, 可以访问相同的数据; (3) 动态性; (4) 结构性:线程是操作系统中的基本调度和分派单位, 它具有唯一的标识符和线程控制块。
线程有5种状态:新建、就绪、运行、等待、终止。
线程的调度: (1) 定义:线程的调度是按照某种原则选择一个线程使它获得处理器运行; (2) 线程调度的方式:采用操作系统剥夺的方式; (3) 常用的剥夺原则 (两种) :一是高优先级线程可以剥夺低优先级线程运行;二是当运行线程时间使用完后被剥夺处理器; (4) 采用剥夺式策略的好处:可以避免一个线程长时间独占处理器。
(二) Java线程对象的创建和管理
Java支持内置的多线程机制, Java语言包中的java.lang.Thread类具有封装线程对象的能力, 用于创建和控制线程。线程对象由Thread类或其子类声明, 线程对象执行的方法是java.lang.Runnable接口中的run () 方法。
1、进程的两种创建方式
Java提供两种方式实现多线程程序设计:继承Thread类和实现Runnable接口。
(1) 直接继承线程Thread类
自定义一个线程类需要声明继承Thread类, 并且必须覆盖Thread类的run () 方法, 直接说明线程对象所执行的操作。这种方式适用于单重继承, 不适用于多重继承。好处是, Thread类的子类具有Thread类声明的方法, 其对象就是线程对象, 可以直接控制和操作。
(2) 实现Runnable接口
Runnable接口中只声明了一个run () 方法, 即只约定了抽象方法run () 是线程的执行方法。在需要多重继承的情况下, 就只能用实现Runnable接口方式。
用一个声明实现Runnable接口的类, 其对象本身不是线程对象, 同时还需要再声明线程对象, 那么, 实现Runnable接口的类的对象是作为一个线程对象的目标对象使用的。
2、Java线程对象的管理
(1) 线程对象的状态及状态管理
一个线程对象被创建后, 总是处于以下六种状态之一:新建态 (仅是一个空对象, 未分配资源) 、就绪态 (启动后) 、运行态 (被调度执行后) 、阻塞态、等待态和终止态 (停止运行未被撤销) 。
Thread类定义了多个改变线程状态的方法, 如: (1) 线程启动:start () 方法启动已创建的线程对象, isAlive () 方法返回线程是否启动的状态; (2) 线程睡眠:sleep () 方法使当前线程停止执行若干毫秒, 线程由运行态进入等待态, 不可运行, 睡眠时间到时线程可再次进入运行态。 (3) 线程中断:interrupt () 方法设置当前线程对象运行中断标记, 与之配合使用的还有两个判断线程是否中断的方法。
(2) 线程对象的优先级管理
Java提供10个等级的线程优先级, 分别用110表示, 优先级最低为1, 最高为10, 默认值为5。Thread类中声明了3个表示优先级的公有静态常量 (用来声明优先级别) 和2个与线程优先级有关的方法 (分别用来获得线程优先级和设置线程优先级) 。
3、精灵线程
定义:本身无法使虚拟主机保持活动的线程被称为精灵线程。精灵线程是应用中典型的独立线程, 任何一个Java线程都能成为精灵线程, 它为同一应用中的其他对象和线程提供服务。精灵线程的run () 方法一般都是无限循环, 等待服务请求。
方法:isDaemon
public final boolean isDaemon ()
返回值:如果是精灵线程, 则返回True, 否则为False setDaemon
public final void setDaemon (boolean daemon)
参数daemon如果为True, 表明将设置当前线程为精灵线程.否则设为用户线程.如果当前线程不能被修改则发出SecurityException异常.
在Thread对象中有一个属性可以设置, 即Daemon属性, 设置成true (缺省为false) , 可以将线程设为精灵线程.
4、线程组
每个Java线程都是某个线程组的成员。线程组提供一种机制, 使得多个线程集于一个对象内, 能对它们实行整体操作。譬如, 你能用一个方法调用来启动或挂起组内的所有线程。Java线程组由ThreadGroup类实现。
当线程产生时, 可以指定线程组或由实时系统将其放入某个缺省的线程组内。一个线程只能属于一个线程组, 并且当线程产生后不能改变它所属的线程组。
(三) Java线程的互斥和同步机制
1、Java的线程互斥机制及实现
如果程序中有几个竞争资源的并发线程, 那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源, 系统中没有饿死和死锁的线程。
Java提供关键字synchronized用于声明一段程序为临界区, 使线程对临界资源采用互斥的使用方式。Synchronized有两种用法:声明一条语句、声明一个方法。两者基本相似, 只是前者的作用范围小, 它只是锁住一条语句 (或复合语句) 而不是完整的方法, 它还指定所要获得锁的对象而不调用方法, 它增加了灵活性并且缩小了对象锁的作用域。
2、Java的线程通信方法及同步机制的实现
线程同步是解决线程间协作关系的手段。线程同步指两个以上线程基于某个条件来协调它们的活动。一个线程的执行依赖于另一个协作线程的消息或信号, 当一个线程没有得到来自于另一个线程的消息或信号时则需等待, 直到消息或信号到达才被唤醒。
多个线程间彼此根据信号量的状态确定该谁执行, 当一个线程开始执行时, 它先要测试信号量的状态, 如果状态合适则执行, 进行相关操作后更改信号量状态, 通知其他等待线程执行;否则等待, 使线程自己处于阻塞状态, 直到被唤醒再次执行。
Java提供了wait () 、notify () 和notifyAll () 方法实现线程间通信, 方法声明如下:
这些方法声明在java.lang.Object类中, 可以被任意类的对象调用, 并且声明为最终方法, 不能被子类覆盖。一个对象调用wait () 方法使当前执行线程变为等待状态, 直到其他线程调用notify () 或notifyAll () 方法通知当前线程才停止等待, 该线程被唤醒。
多线程程序 第10篇
关键词:分布式系统;多任务调度;进程;线程;负载均衡
中图分类号:TP316.4文献标识码:A文章编号:1007-9599 (2011) 06-0000-02
Multi-task Assignment and Process/Thread Adjustment in Distribution System
Yao Dong
(Beijing University of Posts and Telecommunications,BeiJing100083,China)
Abstract:Distributed multi-task operating system distribution and task scheduling,load balancing is difficult to achieve,which processes and threads to achieve the task execution and distribution of one of the most important concepts.How to implement a distributed system processes and threads created,and the interaction between the information is a vital part of distributed system design.On the traditional operating system processes and threads and to compare the realization of the mechanism,further discussed in the distributed operating system,how to multi-task allocation,how the different distribution among the host of the process as well as load balancing.
Keywords:Distributed system;Multi-task scheduling;Process;
Thread;Load balancing
一、引言
分布式系统是计算机发展的一个大趋势,目前云计算、云存储的概念已经逐渐落地,实际上云计算就是分布式系统的一种实现。在分布式系统中,进程(在很多操作系统中也称为任务)是十分重要的概念,是实现任务调度与执行的关键,而线程是轻量级的进程,在响应速度与执行效率上相比进程有很大的改进。在分布式系统中如何实现多任务执行,如何在分布的主机以及CPU上进行创建和分配,涉及到调度策略。另外,如何实现分布式系统中进程间以及线程间的通信,也是需要重点考虑的问题。并且关乎分布式系统执行的效率和效果。
我在对分布式操作系统的研究和学习中发现,许多传统单机操作系统的概念实际上是可以沿用的,但是由于分布式系统自身的特性决定了,这些概念的复用是需要根据分布式系统进行调整和完善的。希望通过本文对传统进程与线程的简单分析和比较,从而探讨如何在分布式环境中进行进程与线程的创建与调度,如何在分布式环境中对多任务进行负载均衡。
二、进程与线程
现代操作系统最基本的构件就是进程。进程是进行系统资源分配、调度和管理的最小独立运行单位,操作系统的各种活动都与进程有关。每个进程是由私有的虚拟地址空间、代码、数据和其它系统资源组成。进程在运行时创建的资源随着进程的终止而死亡。
传统的UNIX进程概念在开发分布式系统应用时已力不从心,这些问题的最好解决之道就是线程,线程推广了进程的概念使一个进程可以包含多个活动。如今,由于线程概念的普及,在UNIX系统中已经普遍实现了线程机制,开发并发应用的程序员现在也可以广泛接触到线程的函数库了。
在传统的Unix模型中,当一个进程需要由另一个实体来执行某件事情时,它就fork一个子进程,让子进程去进行处理。尽管这种模式已经成功使用了很多年,但是仍然暴露出以下问题:
fork开销很大。内存映像需要从父进程拷贝到子进程,所有描述字要在子进程中复制一份,等等。当前的系统实现使用一种称为写时拷贝(copy-on-write)的技术,可以避免父进程数据一开始就向子进程拷贝,直到子进程确实需要自己的拷贝为止。尽管有这种优化技术,但fork的开销仍然很大。
Fork子进程后,需要用进程间通信(IPC)在父子进程间传递信息。Fork之前由父进程准备好的数据很容易传递,因为子进程是从父进程的数据空间及所有描述字的一个拷贝开始的,但是从子进程返回信息给父进程却颇费周折。
线程有助于解决这两个问题。线程有时称为轻权进程,因为线程比进程“权轻”。也就是说,创建线程可能比创建进程快10-100倍:
在一个已存在的进程中创建一个线程比创建一个新进程开销更小。创建新的进程需要进行内存的拷贝操作,这就额外的增加了系统负担,而线程则不需要这个拷贝过程。不过由于现在的操作系统的实现是仅仅当内存需要改变的时候才拷贝改动的部分,所以这里的影响相对还是比较小的。
线程通常用在需要比较好的同步操作的场合。例如,某个问题可以分解为多个几乎对等同步处理的任务的话,则是用线程是很好的选择。进程则适合应用在不需要严格的同步的场合。
线程之间共享数据很方便,因为不同的线程本来就是共享同样的存储空间。而不同进程之间共享数据则需要使用一些IPC机制,例如管道、共享内存、套接字等等。
三、分布式操作系统中进程与线程的创建与调度
事实上,当前使用的操作系统都是把任务分割为进程和线程,分时运行在一个处理器中。这种类似的任务处理方式也可以在很多高性能的应用程序中可以看到,例如数据库引擎、科学计算程序、工作站的工程工具和多媒体程序等。
为了增加处理能力,多数操作系统、应用程序都设计为可以在双处理器和多处理器环境中运行,这样可以利用对称多处理器(SMP)结构将进程和线程放到处理器池中进行运算。通过在一个单芯片中执行两个逻辑CPU,超级线程技术在当前的操作系统和高性能应用中进行进程和线程级的并行处理。多CPU之间进行进程分配的思路与分布式操作系统中进程与线程的创建和调度类似。
在分布式操作系统中,一个新进程的创建分为三个步骤:
1.选择一个目标主机。
2.创建一个执行环境。
3.在执行环境中创建线程。
这三个步骤,除选择目标主机以外,其他两项都与传统的操作系统实现机制相同。需要注意的是,不论分布式操作系统的控制权是如何实现的,作为服务器的主机,必须存放各主机中进程的信息,或者在每个主机上有备份。各目标主机间的通信,以进程级的通信为基础,不同进程间的线程通信,也是通过其主线程进行的。
四、分布式操作系统中进程、线程与多任务分配
一个进程中的所有线程都在该进程的虚拟地址空间中,使用该进程的全局变量和系统资源。操作系统给每个线程分配不同的CPU时间片,在某一个时刻,CPU只执行一个时间片内的线程,多个时间片中的相应线程在CPU内轮流执行,由于每个时间片时间很短,所以仿佛各个线程在计算机中是并行处理的。操作系统是根据线程的优先级来安排CPU的时间,优先级高的线程优先运行,优先级低的线程则继续等待。
在分布式操作系统中,多个应用程序同时运行可以采用两种方式进行:
1.协作式多任务方式。协作这个用语意味着多个应用程序之间必须相互协调,依次实现对操作系统的控制。它们并不是真正的多任务执行,因为其中还有多任务共享系统资源的问题。为了让操作系统把控制权从一个程序转换到另一个程序,当前活动的程序就必须周期地检查一个消息队列。
2.带优先权的多任务方式,称为抢先式多任务。在这种方式下,操作系统可以在需要时中断当前的任务,再按照任务队列中各个任务的优先级别来进行任务的调度。
抢先式多任务执行实际上就是抢先式多线程执行,每个线程有一个优先级值。优先级最低的保留给系统使用。其余的分成四类:空闲、正常、高、和实时。注意这些范围是有重叠的。这样做可使调度更灵活,例如,允许某些后台任务比某些前台任务更重要,尽管在通常情况下,前台任务的优先级应该更高。使用实时优先级时要非常当心。如果你把一个任务的优先级设得太高,也可能无法实现多任务执行功能。这是因为一个任务的优先级太高了,它就完全不允许系统中有其他任务运行。
VMM(虚拟机管理程序)负责在分时抢先的环境里调度各个进程和线程,具体包括以下服务:生成线程、管理线程、撤消线程和线程调度。VMM中有两个调度程序:主调度程序和时间片调度程序。主调度程序负责确定最高优先级的线程。只有最高优先级的线程才会运行,其他优先级较低的都被封锁;时间片调度程序负责为所有具有最高优先级的可运行任务分配时间片。
系统运行过程中,线程的优先级可由系统或设备驱动程序改变。例如,一旦中断产生,则处理这个中断的线程优先级临时提高,以便它立即得到时间来处理该中断。完成后,优先级可以再降低。
五、结束语
在分布式操作系统中,实现多任务分配与传统操作系统的实现机制类似,不同的是,需要在不同主机的进程间进行分配,同时还涉及到多线程的情况。但是,基本的思路是以进程为基础,进程的负荷平衡就能够保证线程负荷均衡。具体进程内部的任务分担可以由进程自己实现,机制可以参考传统操作系统的实现。
参考文献:
[1]W.Richard Stevens.Stephen A.Rago.UNIX环境高级编程[M].北京:人民邮电出版社,2006:242-504
[2]W.Richard Stevens.UNIX网络编程:第2卷,进程间通信[M].北京:人民邮电出版社,2010:141-241
[3]W.Richard Stevens.UNIX网络编程.卷1,套接字联网API[M].北京:人民邮电出版社,2010:423-611
[4]何炎祥.分布式操作系统[M].北京:高等教育出版社,2005:343-436
[5]何炎祥,熊前兴.操作系统原理[M].北京:华中科技大学出版社,2001:125-244
[6]佚名.Windows系统进程全解剖[EB/OL].[2009-12-29].http://www.qqread.com/meetwindows/j486113.html
线程池的多线程并发控制技术研究 第11篇
随着网络技术的发展,为处理大量短小并发任务,线程池技术已经越来越多地被应用到服务器中,并在一定程度上缓解了系统的压力。但伴随着数据量和并发访问量的增加,并发控制与性能已成为需要考虑的一个紧迫问题,特别是中间件如Web应用服务器、事务监控器等。面对突发性的、数量巨大的瞬时客户请求,如何保证服务器端高效的多线程并发控制,是现今研究的一个热点。
1 多线程技术
多线程程序设计是指在单个程序中使用多个线程,这些线程在同一时间并发运行,执行不同的任务。用多个线程同时为多个客户提供服务,这是提高服务器并发性能最常用的手段。使用多线程设计模式可以提高程序的实时响应能力,改进程序的设计结构,更有效地发挥处理器的功能,减少对系统资源的频繁调度和切换[4]。
多线程设计中多个线程访问共享资源时,通过锁定、解锁操作来协调正确的并发操作。Object类的wait()、notify()、notifyAll()方法可以实现控制由一个线程到另一个线程的转移,关键字synchronized机制实施线程的同步。
从线程创建角度看,线程可按照“一客户一线程”、“一请求一线程”、“一对象一线程”等方式创建。从线程来源看,包括“在需要时创建”和“线程池”两种,其中,维护线程池可有效地控制创建和销毁线程带来的开销。
2 线程池比较
线程池出现以前,用单个线程来处理所有请求,或对每一个请求都生成一个新的线程进行处理,增加了服务器创建和销毁线程的开销,系统的响应速度和处理性能都很低。
传统的线程池用到了任务队列和工作队列,利用阻塞原理协调请求和处理之间的速度,在一定程度上缓解了服务器端的压力,提升了系统的性能。
线程池对线程的管理有很多的优点[1],但是传统的线程池也存在问题:第一,当线程池中线程数增加到某个值的时候,增加线程数会导致请求的处理时间线性增长,系统的吞吐量不但不能提高,反而会不断降低;第二,遇到瞬时并发数量大的情况,随应用环境变化的动态调整能力差,不能满足用户请求表现出来的波峰、波谷的曲线要求。
3 多线程的线程池模型
针对传统线程池动态调整策略差不能满足大量瞬时并发客户请求的特点,本文利用JDK1.5并发包设计了能实现多线程并发控制且动态调整的线程池。最近研究的线程池模式有几种[3]:半异步/半同步(Half-Sync/Half-Asyn)模式、领导者/跟随者(Leader/Follower)模式和Replicate 模式。线程池的结构基本包括四个部分[1],如图1所示。
(1) 线程池的管理模块ThreadPoolManager
线程池管理类是线程池结构中的核心类,用于创建并维护线程池。利用JDK1.5本身的构造函数ThreadPoolExecutor()进行设计如下:
Public ThreadPoolExecutor (
int corePoolSize,
int maximumPoolsize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
主要属性说明如下:
corePoolSize:池中所保存的线程数,包括空闲线程;
maximumPoolsize:池中允许的最大线程数;
keepAliveTime:当线程数大于核心数时,此为终止前多余的空闲线程等待新任务的最长时间;
unit:keepAliveTime参数的时间单位,定义为一个对象;
workQueue:执行前用于保持任务的队列,此队列仅保持由execute方法提交的Runnable任务;
threadFactory:执行程序创建新线程时使用的工厂;
handler:由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
(2) 工作线程WorkThread
可以选择实现Runnable接口或者从类Thread中派生。Runnable接口为非Thread子类的类提供了一种激活方式。通过实例化某个Thread实例并将自身作为运行目标,就可以运行实现Runnbale的类而无需创建Thread的子类。
(3) 任务队列TaskQueue
执行前用于保持任务的队列。此队列仅保持execute方法提交的Runnable任务。所有任务队列都可用于传输和保持提交的任务。
(4) 任务接口Task
每个工作线程通过该接口执行指定的任务。比如计算或者是I/O操作。根据任务性质的不同,创建不同的任务接口,放入不同工作线程处理,做到分工明确,提高处理效率。
4 线程池的优化设计
在实际应用中,线程池启动时的各项参数由指定的配置文件config.xml读入,包括线程池中核心线程数的大小、最大线程数的大小、空闲线程生命周期等。面对不同的应用,需要对线程池做出相应的改进、扩展,使线程池更能满足系统的要求。
4.1 合理的设计线程池的大小
合理选择线程池尺寸将会对系统性能产生很大影响。合理的长度取决于未来提供的任务类型和所部署系统的特征,一般由某种配置机制来提供,或者利用Runtime.availableProcessors的结果,动态地进行计算。
线程池的最佳大小取决于可用处理器的数目以及工作队列中的任务性质[5]。在一个具有N个处理器的系统上并且全部是计算性质任务的情况下,线程池具有略大于N的尺寸时会获得最大的CPU利用率;对于任务是需要等待I/O为主的情况下,应让池的大小远超过可用处理器的数目。可以估算出某个典型请求的等待时间(WT)与服务时间(ST)之间的比例,对于一个具有N个处理器的系统,设置大约N(1+WT/ST)个线程将会使处理器获得充分利用。
在实际运行中,可以依据业务情况,进行配置文件的修改以适应实际情况。同时,可以指定是否由线程池管理模块自动进行线程池的优化配置。线程池自动优化配置基于池中线程的使用率来进行,如果排队任务较多,增大线程池的大小,如果很少有任务排队情况发生,则减少线程池中核心线程数,并且可以减小空闲线程生存时间。
4.2 增加及时的动态监控反馈模块
任务有计算密集型,也有I/O操作型,从另一种角度看,当处理队列中的任务时,线程池管理模块可以依据任务的数量或者性质相关性进行处理。
任务数量相关:系统运行时,客户的请求数量表现为一条具有波峰和波谷的不规则曲线,可以依据这条曲线对池进行动态管理。在峰值请求时动态地加大池尺寸提供更多的可用资源;在波谷时减小池尺寸进行资源回收。任务性质相关:为不同任务种类设置不同的工作队列,这样可以在系统总体线程数量不变的情况下,根据线程池的负载情况,为其灵活地调整池尺寸,可以加快处理相同性质线程的速度。还可以依据任务的优先级,将优先级较高的任务提前运行。任务队列中任务优先级的确定可以依据对某些关键数据表、字段的访问或者是具有高等级访问权限的用户。
之前的监控程序都是给池对象增加一个在后台运行的监视器线程,池对象建立后,监视器按照一定的算法检测池对象的运行状态,实现线程池的动态调整。本文中,为了更早地判断池中即将处理的任务性质和数量,在任务队列TaskQueue中设置监控线程,扫描任务队列的任务类型,反馈给池对象的管理者,提前做好处理不同任务的准备。
4.3 多线程的并发控制设计
线程的并发控制机制是利用多个线程可以并发执行的特点,将计算型与I/O型操作重叠起来,从而提高系统并发处理能力。多线程的并发结构分为同步多线程和异步多线程,基于同步多线程的并发结构主要有主/从、对等和流水线3种[3]。
工作线程并不是越多越好,保持适量的工作线程,会提升服务器的并发性能,但当工作线程的数目到达某个极限,超过了系统的负荷时,反而会降低并发性能,使得多数客户无法快速得到服务器的响应[6],如图2所示。
为了更好地与线程池技术结合,本文采用对等结构,保证一个请求不会被多个线程同时处理,限定线程池中同一时刻只有一个线程在监听接收端口。对等结构中所有的线程具有平等的地位,都可以接收用户请求,而且在接收到请求后并不转发给其他线程而是直接处理。因为LF模式比HH模式有着较大的性能优势[3],所以设计的线程池采用领导者/跟随者模式来实现Peer结构。
4.4 线程池动态调整策略
正常运行情况下,线程池中如果运行的线程少于核心线程数corePoolSize,则始终首选添加新的线程,而不进行排队;如果运行的线程数等于或多于corePoolSize,则始终首选将请求加入队列,而不添加新的线程;如果无法将请求加入队列,则创建新的线程,除非创建此线程超过线程池设定的最大线程数maximumPoolSize,在这种情况下,任务将被拒绝。这些调整可以通过监测线程池中线程的使用率来实现并完成。
当服务器端接收到客户瞬时大量的实时请求时,任务队列的监控线程会把这个信息及时反映给管理线程。这个时候,系统的过载保护功能将在保证系统动态负载平衡的前提下,开启Capacity Constraint容量限制,容量限制数目为等待请求和处理请求之和。当请求数目到达这一容量限制时,服务器端会拒绝前端客户请求,这样保障了服务端不会被资源过度消耗,达到系统服务可靠的目的。线程池基于历史吞吐量、队列大小和任务类型,自动增减并发的线程数,调整线程池的容量,能够最大化线程池的吞吐量。
5 性能分析及测试
5.1 最佳性能时的并发线程数
采用HF模式线程池技术后,预期收益[2]E(n)可以用如下公式表示:
式中:n是线程池中线程数;r是随机变量,表示当前正在并发运行的线程数,p(r)是r的概率密度函数,p(r)取决于客户请求到达的方式;c1是创建一个线程的开销;c2是单个线程的管理开销。
设定每一个用户具有同等的概率去发送同样多的请求数,也就是说,每一个客户都可以看成是完全相同的,从而保证了线程池中并发运行的线程数服从均匀分布Uniform(0,N),所以得出并发用户数N一定时线程池性能达到最大值时的线程数n*,满足公式:
5.2 性能测试
考虑到用户和服务器的要求,衡量线程池性能的是处理用户提交的任务所花的时间长短和同时并行处理多个用户请求时系统吞吐量大小。
实验测试包括服务器和客户端两部分。服务器运行线程池来等待处理请求,所用的机器CPU主频是Intel P4 2.8G,内存512M,操作系统是Windows Server 2003;客户端机器CPU主频是Intel P4 2.8G,内存512M,操作系统是Windows XP。使用Mercury LoadRunner 8.0来模拟客户发送服务请求,虚拟用户数,所取的值分别为2、4、8、16、32、64和100,每一个虚拟客户发送1000个服务请求,而且在收到上一个请求的应答后才发送下一个请求。所有虚拟客户在一次运行过程中都发送同样的请求。
实验对比传统普通的线程池,设定线程池中核心线程数分别为2、4和8,最大线程数为20,空闲生存时间为1。用户请求的服务分别为计算密集型和I/O密集型。计算密集型请求需要的处理时间是2ms,而I/O密集型需要的处理时间是10ms。由实验数据得到对比图如图3-图6所示。
5.3 结 论
由以上实验数据得知,改进后的线程池在动态调整线程池大小和对多线程的并发控制上比传统的线程池有较大的优化。由图3可以看出,改进后的线程池比传统的线程池处理任务更好,当系统中并发线程数增加时,性能有所下降。由公式(2)和图4可以得出,在不同的并发用户数下线程池性能达到最大时的线程数,当线程池内核心线程为8,并发数为8时,系统的性能最好。而当并发线程为2、4时系统还不能完全把性能提到最好,核心线程数大于8时系统负荷加大,系能有所下降。对于不同性质或者数量工作的处理,图5和图6可以看出不同线程数目下改进的线程池对两类任务的处理,系统性能之间的对比(A、B分别是改进后和传统的线程池,2、4、8分别是并发线程数),改进后的线程池完全能满足系统的需要。
6 结束语
线程池技术发展日趋成熟,越来越向大型分布式应用方向发展,功能要求越来越高,实现过程也更严格。多线程技术使线程池可以并发的执行更多的任务,而对线程高效的并发控制策略可以提高系统的并行处理能力,改善交互响应时间。本文采用高效的多线程并发控制策略,对传统的线程池进行了部分优化。通过实验对比,改进后的线程池,能满足系统多线程对不同性质和数量请求的并发控制,提高了系统的整体性能。
摘要:当服务器端面临突发性且数量巨大的瞬时客户端请求时,传统线程池暴露了诸多弊端。通过改进传统线程池,对多线程采取了更好的同步并发控制策略。通过实验对比,改进后的线程池比传统线程池更能提升系统性能,提高处理不同数量或性质的请求效率。
关键词:线程池,多线程,同步,并发控制
参考文献
[1]王华.线程池技术研究与应用[J].计算机应用研究,2005(11):141-142.
[2]Ling Y,Mullen T,Lin X.Analysis of optimal thread pool size[C]//New York,USA:ACM Operating Systems Review,SIGOPS,ACMPress,2000,34(2):42-55.
[3]李刚,金蓓弘.基于线程的并发控制技术研究与应用[J].计算机工程,2007,30(14):43-45.
[4]高正光,李启炎.一种多线程并发环境下的对象缓存模型[J].计算机工程,2005,31(22):104-106.
[5]Brian Goetz.Java并发编程实践[M].北京:电子工业出版社,2007.
多线程程序范文
声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。如若本站内容侵犯了原著者的合法权益,可联系本站删除。


