线程的结束方式:变量和事件
线程的通信方式:变量和消息
线程的两个组成部分:线程的内核对象和线程栈
/************** 引入线程优点******************/
(1)易于调度
(2)提高并发性
(3)开销小;创建线程比创建进程快,所需要的开销也小;
(4)有利于发挥多处理器的功能。通过创建多线程,每个线程都在一个处理器上运行,从而实现应用改程序的并行,使每个处理器得到充分运行。
/**************多线程模型******************/
多线程模型:适用于任务少,时间持续长;
/**************线程池模型******************/
线程池:预先创建一些线程(cpu*2),使它们处于睡眠状态(等信号量),当任务来临时,唤醒线程(释放信号量);
线程池适用于,任务量大,持续时间短;
线程池注意事项:1.线程个数为cpu*2;2.线程并发问题;3.内存泄露问题;4.死锁;
死锁:两个及以上线程,在竞争资源,产生一种互相僵持现象,若无外力作用,无法推进;
/**************线程和进程的区别******************/
(1)一个线程必定属于也只能属于一个进程;而一个进程可以拥有多个线程并且至少拥有一个线程;(2)属于一个进程的所有线程共享该线程的所有资源,包括打开的文件,创建的socket等,不同的进程是相互独立;
(3)进程有进程的控制块,线程也有自己的线程控制块。但线程控制块要比进程控制块小得多。线程间切换代价小,进程间切换代价大。
(4)进程是程序的一次执行,线程可以理解为程序中一段程序片段的执行。
(5)每个进程都有独立的内存空间,而线程共享其属进程的内存空间;
第二种说法:
1) 调度。在传统的操作系统中,拥有资源和独立调度的基本单位都是进程。在引入线程的操作系统中,线程是独立调度的基本单位,进程是资源拥有的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。
2) 拥有资源。不论是传统操作系统还是设有线程的操作系统,进程都是拥有资源的基本单位,而线程不拥有系统资源(也有一点必不可少的资源),但线程可以访问其隶属进程的系统资源。
3) 并发性。在引入线程的操作系统中,不仅进程之间可以并发执行,而且多个线程之间也可以并发执行,从而使操作系统具有更好的并发性,提高了系统的吞吐量。
4) 系统开销。由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、 I/O设备等,因此操作系统所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程CPU环境的保存及新调度到进程CPU环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。此外,由于同一进程内的多个线程共享进程的地址空间,因此,这些线程之间的同步与通信非常容易实现,甚至无需操作系统的干预。
/**************线程的几种状态******************/
/**************消息响应机制******************/
/**************线程的内核对象******************/
/**************运行线程的退出******************/
/************** CreateThread()和_beginthread()比较 ******************/
_beginthreadex是微软的C/C++运行时库函数,CreateThread是操作系统Windows的函数。
_beginthreadex通过调用CreateThread来实现的,但比CreateThread多做了许多工作。
注意:若要创建一个新线程,绝对不要使用CreateThread,而应使用_beginthreadex.在调用C/C++运行库函数时,那些函数必须直到去查找主调线程的数据块,从而避免影响到其他线程。
Why?考虑标准C运行时库的一些变量和函数,如errno,这是一个全局变量。全局变量用于多线程会出什么事,你一定知道的了。故必须存在一种机制,使得每个线程能够引用它自己的errno变量,又不触及另一线程的errno变量._beginthreadex就为每个线程分配自己的tiddata内存结构(从C/C++运行库的堆上分配的)。该结构保存了许多像errno这样的变量和函数的值、地址。通过线程局部存储将tiddata与线程联系起来。结束线程使用函数_endthreadex函数,释放掉线程的tiddata数据块。
假如调用CreateTread创建线程,当一个线程调用需要_tiddata结构的 C/C++运行函数时,C/C++运行库函数尝试去得线程数据块的地址(通过TlsGetValue)。如果NULL被作为_tiddata块的地址返回,表明主调线程没有与之关联的_tiddata块。在这个时候C/C++运行库函数会主动为主调线程分配并初始化一个_tiddata块。带来的问题:①假如线程使用了C/C++运行库的signal函数,则整个进程都会终止,因为结构化处理(SEH)帧没有就绪②假如线程是通过调用_endthread来终止,数据块不能被销毁,从而导致内存泄露。
/1. 创建线程; CreateThread
HANDLE m_hthread = CreateThread(NULL, //安全属性
0 ,//默认 1MB
&CMy0911ThreadDlg::ThreadProc, //线程函数地址
this, //线程参数,ThreadProc,这个函数中的形参lpParameter;
0 ,//创建标识 0立即运行 CREATE_SUSPENDED 挂起
NULL //线程ID
);
//下方函数的地址是线程函数地址
DWORD WINAPI ThreadProc( LPVOID lpParameter) //线程的入口函数
{
//TODO:
return 0;
}
//线程挂起;
SuspendThread(m_hthread);
//线程恢复;
ResumeThread(m_hthread);
//关闭线程句柄;
CloseHandle(m_hthread);
//强制杀死;(需要线程句柄)
if(WAIT_TIMEOUT == WaitForSingleObject(m_hthread,200))
{
//2.强制杀死
TerminateThread(m_hthread,-1);
}