Win32 多线程程序设计(一)概念

1.1 进程VS线程

1.1.1 进程

进程包含内存和资源,其中资源包括核心对象(HANDLE,线程等)、USER资源(对话框、字符串等)、GDI资源(DEVICE CONTEXT、BRUSH等);

注:进程本身并不能够执行,他只是提供一个安置内存和线程的地方;
注:UNIX中,进程与其主线程是相同的东西。

1.1.2 线程

进程和内存并没有真正做什么事情,CPU开始执行代码时,产生进程的同时会产生一个线程,同一时间同一个进程可以拥有一大堆线程执行同一段代码。

线程可以共享所属进程中内存数据,如变量等。

1.1.3 多线程好处

比如1:拷贝文件,如果是非多线程,则拷贝进度条会一直悬在那儿一动不动,直到文件拷贝完成;程序员也无法改善这个UI操作局面,因为操作停滞在操作系统里面。如果是多线程,即是停滞在程序中而非操作系统上,则可以利用多线程技术循环来获取拷贝进度,从而更新进度条,但是对于操作系统没有太多的控制能力。

比如2:读取光驱内容,在程序搜索光驱内容时,CPU时间都被浪费了,其实这段时间可以用来更新屏幕等其他活动。使用多线程可以去除许多长久以来存在于光驱和网络上的“瓶颈”;

1.1.4 为什么不用多进程

线程:线程廉价,线程启动快,退出也快,对系统资源冲击较小,线程彼此分享了大部分核心对象(如HANDLE)的拥有权;

进程:为了避免某个进程危及另一个进程的资源,如HANDLE,HANDLE只在其诞生进程中才有意义,这种情况下,无法在进程间共享HANDLE,为了达到进程共享的目的,需要产生一个HANDLE的副本,而同一进程的多线程中,所有线程都可以使用这个HANDLE,因为HANDLE和线程位于同一个进程中。

多进程:如WEB服务响应,为每一个请求产生一个进程很容易,但是需要非常惊人的额外负担,包括必须载入服务器软件的一个完全新副本,并配置大量的内存进行初始化,同时将副本状态调整到与原状态相同,这可能需要数秒的时间,请求完成后,又需要释放进程。

单线程:如果用单一线程处理所有请求服务,一来硬件无法发挥最佳使用率,二来对于多个请求,后面的请求需要长时间等待前面的请求都处理完;

1.2 内存

相当于活页夹中的纸,大致可分为三类:

Code:只读,程序的可执行部分,CPU唯一允许执行的内存;

Data:程序中的所有变量(不包括函数中的局部变量),包括全局变量、静态变量、MALLOC或NEW动态分配的内存(自由存储区)。

Stack:调用函数时所用的堆栈空间,其中有局部变量(函数执行完释放),每个线程产生时配有一个堆栈。

1.3 Context Switching

多任务系统中,操作系统必须确保每个线程都有机会被执行;即当硬件计时器认为某个线程已经执行够久了,就会发出一个中断,于是CPU取得当前线程的所有状态并将其拷贝到堆栈中,再将其从堆栈中拷贝到CONTEXT(存储线程状态)中,备后续被调用;

Context Switch可能在一秒钟之内发生数百次,快速切换容易给人造成错误是电脑同时在做多件事,而时间上CPU一次只能做一件事(单核CPU情况下),多核CPU可以同时做多件事;

效率:CPU进行线程切换,需要付出一点效率,即“两个线程同时计算PI”所花费的时间比“一个线程两次做两次一样的工作”所花费的时间长一点

1.4 Race Conditions(竞争条件)

抢先式多任务系统中,两线程之间的执行次序变得不可预期;

1.5 Atomic Operations(原子操作)

一条C指令会被扩张为多条机器指令,一个操作(一条或多条C指令)如果不受中断的完成,则称之为原子操作,如果被中断,则产生一个race conditon;

1.6 核心对象

Thread*(线程)

当线程结束时,线程对象即被激发。当线程还在进行时 , 则 对 象 处 于 未 激 发 状 态 。 线 程 对 象 系 由CreateThread() 或 CreateRem oteThread() 产生

Process*(进程)

当进程结束时,进程对象即被激发。当进程还在进行时 , 则 对 象 处 于 未 激 发 状 态 。 CreateProcess() 或OpenProcess() 会传回一个进程对象的 handle

Change Notification

当一个特定的磁盘子目录中发生一件特别的变化时,此对象即被激发。此对象系由 FindFirstChangeNotification()产生

Console Input*

当 console 窗口的输入缓冲区中有数据可用时,此对象将处于激发状态。CreateFile() 和 GetStdFile() 两函数可以获得 console handle。

Event*

Event 对象的状态直接受控于应用程序所使用的三个Win32 函数:SetEvent()、PulseEvent()、ResetEvent()。CreateEvent() 和 OpenEvent() 都可以传回一个 eventobject handle 。Event 对象的状态也可以被操作系统设定——如果使用于“overlapped”操作时

Mutex*

如果 mut ex 没有被任何线程拥有,它就是处于激发状态。一旦一个等待 mut ex 的函数返回了,mutex 也就自动重置为未激发状态。CreateMutex() 和 OpenMutex()都可以获得一个 mutex handle。

Semaphore*

Semaphore 有点像 mut ex,但它有个计数器,可以约束其拥有者(线程)的个数。当计数器内容大于 0 时,semaphore 处于激发状态,当计数器内容等于 0 时,semaphore 处于未激发状态。CreateSemaphore() 和OpenSemaphore() 可以传回一个 semaphore handle 。

发表回复