3、多线程(1)
一、概念
1、进程:通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动。一个程序可以有多个进程实例。
2、进程由两个部分组成:
1)操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。
2)地址空间。它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。
进程从来不执行任何东西,它只是线程的容器。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码。
单个进程可能包含若干个线程,这些线程都“同时”执行进程地址空间中的代码。
每个进程至少拥有一个线程,来执行进程的地址空间中的代码。当创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主线程。此后,该线程可以创建其他的线程。
一个进程不能读取、写入、或者以任何方式访问驻留在该分区中的另一个进程的数据。
3、线程由两个部分组成:
1)线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。
2)线程堆栈,它用于维护线程在执行代码时需要的所有参数和局部变量。
线程总是在某个进程环境中创建。系统从进程的地址空间中分配内存,供线程的堆栈使用。新线程运行的进程环境与创建线程的环境相同。因此,新线程可以访问进程的内核对象的所有句柄、进程中的所有内存和在这个相同的进程中的所有其他线程的堆栈。这使得单个进程中的多个线程确实能够非常容易地互相通信。
线程只有一个内核对象和一个堆栈,保留的记录很少,因此所需要的内存也很少。因为线程需要的开销比进程少,因此在编程中经常采用多线程来解决编程问题,而尽量避免创建新的进程。
操作系统为每一个运行线程安排一定的CPU时间 —— 时间片。
二、线程编程
用CreateThread来创建线程。
Main函数是主线程的入口函数。新创建的线程也需要一个入口函数,就在下面的lpStartAddress中定义。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
DWORD dwStackSize, // initial stack size
LPTHREAD_START_ROUTINE lpStartAddress, // thread function
LPVOID lpParameter, // thread argument
DWORD dwCreationFlags, // creation option
LPDWORD lpThreadId // thread identifier
);
lpThreadId 用来接收线程的ID。
DWORD WINAPI ThreadProc( LPVOID lpParameter );
类型和参数要匹配,函数名任意。
关闭线程句柄
BOOL CloseHandle( HANDLE hObject );
注意:关闭句柄并没有终止新创建的线程。只是表示对新创建的线程的引用不敢兴趣,系统会递减新
线程的线程内核对象的使用计数。当使用计数为0的时候,系统就会释放线程内核对象。如果在主线程中没有关闭线程的句柄,始终都会保留一个引用。这样线程内核对象的引用计数就不会为0。即使新线程执行完毕,线程内核对象也不会被释放,只有等到进程终止的时候系统才会为残留的对象做清理工作。所以应该在不再使用线程的句柄的时候将其关闭掉,让线程的线程内核对象的引用计数减1。
每个线程有一个引用记数器,当创建成功时初值为2,当你调用一次CloseHandle()时,计数器减一,如果为0,清除线程。线程执行完成计数器还是要减一,如果为0,清除线程。
暂停线程的执行
当线程暂停执行的时候,也就是表示它放弃了执行的权力。操作系统会从等待运行的线程队列中选择一个线程来运行。新创建的线程就可以得到运行的机会。
可以使用函数Sleep:
void Sleep( DWORD dwMilliseconds //sleep time 以毫秒为单位 );
暂停当前线程指定时间间隔的执行。
参考
[1] 孙鑫 《深入VC++》