线程基础知识-产生、退出、状态切换

来源:互联网 发布:ant java arg 编辑:程序博客网 时间:2024/06/16 03:12

线程的结束方式:变量事件

线程的通信方式:变量消息

线程的两个组成部分:线程的内核对象和线程栈

/************** 引入线程优点******************/

(1)易于调度

(2)提高并发性

(3)开销小;创建线程比创建进程快,所需要的开销也小;

(4)有利于发挥多处理器的功能。通过创建多线程,每个线程都在一个处理器上运行,从而实现应用改程序的并行,使每个处理器得到充分运行。

/**************多线程模型******************/

多线程模型:适用于任务少,时间持续长;

/**************线程池模型******************/

线程池:预先创建一些线程(cpu*2),使它们处于睡眠状态(等信号量),当任务来临时,唤醒线程(释放信号量);

线程池适用于,任务量大,持续时间短;

线程池注意事项:1.线程个数为cpu*22.线程并发问题;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

        );

unsigned long _beginthread(   //成功返回新线程句柄,失败返回-1;
void( *start_address )( void * ),    //线程函数入口地址
unsigned stack_size,  //线程栈大小
void *arglist    //传递给线程的参数列表,无参则NULL。
);

//下方函数的地址是线程函数地址

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);

    }

原创粉丝点击