多线程

1.        程序、进程和线程

         程序是计算机指令的集合,它以文件的形式存储在磁盘上。进程通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动。

         进程是资源申请、调度和独立运行的单位,使用系统中的运行资源。进程由两个部分组成:操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。地址空间。它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。进程从来不执行任何东西,它只是线程的容器。单个进程可能包含若干个线程,每个进程至少拥有一个主线程,来执行进程的地址空间中的代码,该线程可以创建其他的线程。系统赋予每个进程独立的虚拟地址空间。对于32位进程来说,这个地址空间是4GB。每个进程有它自己的私有地址空间。4GB是虚拟的地址空间,只是内存地址的一个范围。在你能成功地访问数据而不会出现非法访问之前,必须赋予物理存储器,或者将物理存储器映射到各个部分的地址空间。4GB虚拟地址空间中,2GB是内核方式分区,供内核代码、设备驱动程序、设备I/O高速缓冲、非页面内存池的分配和进程页面表等使用,而用户方式分区使用的地址空间约为2GB,这个分区是进程的私有地址空间所在的地方。

         线程由两个部分组成:线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。线程只有一个内核对象和一个堆栈,保留的记录很少,因此所需要的内存也很少。操作系统为每一个运行线程安排一定的CPU时间—— 时间片。系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行。

利用WaitFor函数族(WaitForSingleObject之类),根据Event状态来使线程挂起,或者是用Sleep函数让线程挂起一段时间。阻塞的函数如WaitForSingleObject, recv,accept等,这类的阻塞是会让出CPU的。

2.    多线程的优缺点

l  多线程优点:

简化异步事件的处理,当一个服务器应用程序在接收不同的客户端连接时最简单地处理方法就是为每一个客户端连接建立一个线程。然后监听线程仍然负责监听来自客户端的请求。如果这种应用程序采用单线程来处理,当监听线程接收到一个客户端请求后,开始读取客户端发来的数据,在读完数据后,read方法处于阻塞状态,也就是说,这个线程将无法再监听客户端请求了。而要想在单线程中处理多个客户端请求,就必须使用非阻塞的Socket连接和异步I/O。但使用异步I/O方式比使用同步I/O更难以控制,也更容易出错。因此,使用多线程和同步I/O可以更容易地处理类似于多请求的异步事件。

用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度,用户界面可以在进行其它工作的同时一直处于活动状态。

优化设计,提高CPU的使用率。当前没有进行处理的任务时可以将处理器时间让给其它任务;占用大量处理时间的任务可以定期将处理器时间让给其它任务;可以随时停止任务;可以分别设置各个任务的优先级以优化性能。

l  多线程缺点

等候使用共享资源时造成程序的运行速度变慢。这些共享资源主要是独占性的资源,如打印机等。

对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。当这种负担超过一定程度时,多线程的特点主要表现在其缺点上,比如用独立的线程来更新数组内每个元素。

线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。

对公有变量的同时读或写。当多个线程需要对公有变量进行写操作时,后一个线程往往会修改掉前一个线程存放的数据,从而使前一个线程的参数被修改;另外 ,当公用变量的读写操作是非原子性时,在不同的机器上,中断时间的不确定性,会导致数据在一个线程内的操作产生错误,从而产生莫名其妙的错误,而这种错误是程序员无法预知的。

3.        线程的创建

在创建完一个线程后,务必使用CloseHandle关闭句柄。CloseHandle关闭句柄并没有终止新创建的线程。只是表示对新创建的线程的引用不敢兴趣,系统会递减新线程的线程内核对象的使用计数。当使用计数为0的时候,系统就会释放线程内核对象。如果在主线程中没有关闭线程的句柄,始终都会保留一个引用。这样线程内核对象的引用计数就不会为0。即使新线程执行完毕,线程内核对象也不会被释放,只有等到进程终止的时候系统才会为 残留的对象做清理工作。所以应该在不再使用线程的句柄的时候将其关闭掉,让线程的线程内核对象的引用计数减1。

线程函数只能是全局函数或静态对象成员函数。

4.        线程间同步

互斥对象

n  互斥对象(mutex)属于内核对象,它能够确保线程拥有对单个资源的互斥访问权。

n  互斥对象包含一个使用数量,一个线程ID和一个计数器。

n  ID用于标识系统中的哪个线程当前拥有互斥对象,计数器用于指明该线程拥有互斥对象的次数。

n  为协调共同对一个共享资源的单独访问而设计。互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。

n  CreateMutex:第二个参数为TURE,表示该线程拥有该对象;为FALSE表示为有信号状态。ReleaseMutex:谁获得谁释放。如果由其他线程释放会失败。

事件对象

n  事件对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。n  有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。

关键代码段

  关键代码段(临界区)工作在用户方式下。

  关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。

  通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。

信号量

为控制一个具有有限数量用户资源而设计。信号量允许多个线程同时使用共享资源。

各种对象关系的比较

  关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。

  互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部使用的话使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它。

  互斥量,信号量,事件都可以被跨越进程使用来进行通报数据操作,而其他的对象与数据同步操作无关,但对于进程和线程来讲,如果进程和线程在运行状态则为无信号状态,在退出后为有信号状态。所以可以WaitForSingleObject来等待进程和线程退出。

  WaitForSingleObject:等到有信号状态。是否有信号,看内核对象是否属于该线程。线程终止时,会将互斥对象引用计数为0,并释放互斥对象,通过返回值可以知道是否是这种情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值