1.进程是一个正在运行程序的实例,它从不执行任何东西,只是线程的容器。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程地址空间中的代码。
2.当创建一个进程时,操作系统会自动创建这个进程的第一个线程,即主线程,即执行main函数或winMain函数的线程,main函数或winMain函数是主线程的进入点函数,此后,该线程可以创建其他的线程。
3.系统赋予每个进程独立的虚拟地址空间,对于32位进程来说,这个地址空间2的32次幂即4GB。
4.每个进程有它的私有地址空间。假设进程A有一个存放在它地址空间的数据结构,地址为0×12345678,进程B有个不同的数据结构存放在它的地址空间中,地址是0×12345678,当进程A中运行的线程访问地址为0×12345678的内存时,访问的是A的数据结构,当进程B中运行的线程访问地址为0×12345678的内存时,访问的是B的数据结构。进程A中运行的线程不能访问进程B的地址空间中的数据结构。
5.页文件在磁盘上划分一块空间当作内存使用,增加内存数量,这样的内存叫作虚拟内存。
6.4GB虚拟地址空间中,2GB是内核方式分区,供内核代码设备驱动程序等使用,而用户方式分区的地址空间为2GB,这个分区是进程的私有地址空间。一个进程不能以读取、写入或以任何方式访问驻留在该分区中的另一个进程的数据。
7.线程组成,线程由两部分组成
1) 线程内核对象。当创建一个线程时,系统会创建一个线程内核对象,这个对象不是线程本身,而是操作系统用来管理线程的较小的数据结构。
2) 线程堆栈。用于维护线程在执行代码时所需的所有参数和局部变量。
8.线程只有一个内核对象和一个堆栈,所需的内存很少。因为线程需要的开销比较少,因此在编程时经常采用多线程来解决问题,而尽量避免创建新的进程。
9.操作系统为每一个运行线程安排一定的CPU时间片。线程在自己的时间片内运行,因时间片相当短,因此给用户的感觉就像线程同时运行一样。当计算机拥有多个CPU,线程就能真正的同时运行了。
10.在多线程程序中,主线程结束程序就退出,因此使用sleep函数使主线程睡眠,则执行其他线程,其他线程交替执行。
11.线程交替执行时,当一个线程开始执行,是从它上次暂停的位置开始执行,如果多个线程共用资源,有时造成错误,因此要使用线程同步机制。
12.互斥对象
1) 互斥对象属于内核对象,它能确保线程拥有对单个资源的互斥访问权。
2) 互斥对象包括一个使用数量、一个线程ID、一个计数器。
3) ID用于标识系统中哪个线程当前拥有互斥对象,计数器用于指明该线程拥有互斥对象的次数。
13.创建互斥对象函数CreateMutex
1) 第二个参数表示创建互斥对象的线程是否拥有互斥对象。
2) 因为互斥对象是共有的,所以一般声明为全局变量。
3) 第三个参数指定互斥对象名次,为null则匿名互斥对象,不为nul则是命名互斥对象,当是命名互斥对象时,如果该互斥对象先前已被创建,则返回已创建的互斥对象,创建互斥对象后调用GetLastError函数,根据GetLastError返回值判断该互斥对象是否被创建。利用这一特点可保证应用程序只有一个实例在运行。
14.请求互斥对象函数WaitForSingleObject
1) 第一个参数是请求的互斥对象的句柄,当该互斥对象有信号状态时,函数返回。
2) 第二个参数是时间,当到了这个时间时,不管请求的互斥对象是否有信号,函数返回,设为0则立即返回,设为INFINITE则一直等待请求对象的信号状态。此时调用这个函数的线程暂停执行,等待互斥对象。
3) 当没有线程拥有互斥对象时,互斥对象为有信号状态。
4) 当拥有互斥对象的线程执行后,要释放互斥对象,调用ReleaseMutex函数,如果-1后的计数器为0,操作系统会将互斥对象的线程ID设为0,并设为有信号状态,以便其他线程使用。
15.释放互斥对象函数ReleaseMutex
当释放互斥对象时,操作系统会比较调用这个函数的线程ID与当前拥有互斥对象的线程ID是否相同,不同的不允许释放,因此互斥对象是谁拥有谁释放(不是谁创建谁释放)。
16.当创建互斥对象时,计数器设为1,当请求互斥对象时,计数器+1,释放时-1。如果在一个线程中多次请求互斥对象,就要多次释放。
17.如果一个线程请求互斥对象后未释放,当拥有该线程终止后,操作系统会自动释放互斥对象。
通过请求互斥对象的返回值可判断请求到的互斥对象是被正常释放的还是其拥有者终止后被操作系统释放的。