您的位置:首页 > 其它

线程的概念&线程控制&分离线程

2017-06-08 15:23 351 查看
1、线程

(1)线程的概念

      一般来说,我们把正在计算机中执行的程序叫做"进程"(Process) ,而不将其称为程序(Program)。所谓"线程"

(Thread),是"进程"中某个单一顺序的控制流。某些地方用轻量级进程(Lightweig
ht Process)来代替线程,就象进程

一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量

但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其它每个进程应有的状态

线程与真正进程的相似性在于它们都是单一顺序控制流。然而线程被认为轻量是由于它运行于整个程序的上下文内,能

使用整个程序共有的资源和程序环境。作为单一顺序控制流,在运行的程序内线程必须拥有一些资源作为必要的开

销。例如,必须有执行堆栈和程序计数器。在线程内执行的代码只在它的上下文中起作用,因此某些地方用"执行上下

文"来代替"线程"。

其实,没有真正意义上的线程,Linux下用进程来模拟线程。

 由于同一进程的多个线程共享同一地址空间,因此代码和数据都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一 个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

1)文件描述符 

    2)信号处理方式 

    3)当前工作目录 

    4)用户id和组id

但是以下资源就是线程独享的:

1)线程id 
2)上下文,包括各种寄存器的值、程序计数器和栈指针 

3)栈空间 

4)errno变量 

5)信号屏蔽字 

6)调度优先级

(2)线程的特点:

1)一个进程可以产生多个线程,线程也可以产生另一个线程;

2)一个进程里的所有线程共享该进程独有的虚拟内存地址空间以达到共享内存,这是进程之间做不到的;

3)一个线程在使用某些某些共享内存时,其他内存必须等待,当然也可能是能供给固定数目的线程,当多余该数目

时也需要等待;

4)每个线程都有私有栈结构、私有的上下文信息。

(3)线程与进程的联系与区别:

线程是进程的一个执行分支;线程在进程内部运行,本质上是在进程内同一地址空间里运行。Linux下CPU所看到

的所有进程可以看做轻量级进程(LWP)。一个进程可以拥有多个线程,它们可以利用进程所拥有的资源,一个

线程必须有一个父进程。线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程

所拥有的全部资源。线程可以创建和撤消线程,从而实现程序的并发执行。一般,线程具有就绪、阻塞和运行三种

基本状态。进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。一个进程中的多个线程共享相同的

内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。

线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己

的执行堆栈和程序计数器为其执行上下文。多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定。线程

的运行中需要使用计算机的内存资源和CPU。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单

位,而把线程作为CPU或操作系统独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资

源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系

统资源的利用率和吞吐量。

综上所述,可把线程与进程的区别可以归纳为以下几点:

1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其

它进程不可见。

2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥

手段的辅助,以保证数据的一致性。

3)调度和切换:线程上下文切换比进程上下文切换要快得多。

4)在多线程OS中,进程不是一个可执行的实体。

(4)再来看一下引入线程的好处:

1) 创建一个新线程花费的时间少。

2 )两个线程的切换时间少。

3) 由于同一个进程内的线程共享内存和文件,所以线程之间互相通信必须调用内核。

4) 线程能独立执行,能充分利用和发挥处理机与外围设备并行工作的能力。
2、线程控制(创建线程、终止线程、等待线程)

     
我们要学习的线程库函数是由POSIX标准定义的,称为POSIX thread或者pthread。在Linux 上线程函数位于

libpthread共享库中,因此在编译时要加上-lpthread选项。

Makefile的编写:



(1)创建线程 :pthread_create()

函数原型:int pthread_creat(pthread_t * thread,const pthread_attr_t * attr,void *(*start_routine)(void *),void
*arg);

thread: 参数是一个指针,当线程成功创建时,返回创建线程ID。 

attr: 表示线程的属性,一般设置为NULL,表示线程属性取缺省值;

start_routine: 该参数是一个函数指针,指向线程创建后要调用的函数,决定新线程所执行的代码。 

arg: 传递给线程函数的参数。 

返回值:创建线程成功返回0,失败返回错误码。 

pthread_create成功返回后,新创建的进程的ID被填写到thread参数所指向的内存单元。

实例:





运行结果:



我只截了一部分,结果一直会出现出现上面的内容。

(2)进程等待pthread_join()

函数原型:int pthread_join(pthread_t thread,void **retval);

retval是void*类型,和线程函数返回值得用法一样,其他线程可以调用pthread_join获得这个指针。

返回值:成功返回0,失败返回错误码。

调用该函数的线程将挂起等待,直到ID为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到

的终止状态是不同的,总结如下: 

1) 如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。 

2)如果thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元.里存放的是常数

PTHREAD_CANCELED(pthread库中一般是-1)。

3) 如果thread线程是.自.己调.用pthread_exit终.止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。 

如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数 

pthread_join是采用以阻塞方式等待

它的作用是: 

a.保证退出时的退出顺序 

b.回收新线程退出时的资源情况 

c.获得新线程退出时结果是否正确的退出返回值

实例:



运行结果4S后输出:



(3)终止线程pthread_cancel()\pthread_exit()

pthread_exit()函数原型:void pthread_exit(void *retval);

如果需要只终止某个线程而不终止整个进程,可以有三种方法: 

1)从线程函数return。这种方法对主线程不适用,从main函数return相当于exit。主线程退出,所有线程都会退出

2) 线程还可以调用pthread_cancel终止同一进程中的另一个线程。进程取消,退出结果为-1.  

3)一个线程可以调用pthread_exit来终止自己 

注意1:这里不能使用exit函数来结束线程,因为他是用来结束进程的。

注意2:pthread_exit或者return返回的指针所指的内存单元必须是全局的或者用malloc分配的,不能在线程函数的

栈上分配,因为其他线程得到这个返回指针时线程函数已经退出了。

实例:



运行结果:



这个时候的返回值是-1的原因是:如果thread线程被别的线程调.用pthread_cancel异常终掉,value_ptr所指向的单元.里存放 的是常数PTHREAD_CANCELED。在Linux的pthread库中常数PTHREAD_CANCELED的值是-1。

    

   一般情况下,线程终止后,期终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是进程也可以被置为detach状态,这样的线程一旦终止就立刻回收它所占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join或pthread_detach,这样调用将返回EINVAL。对一个尚未detach的线程调用pthread_join或pthread_detach都可以把该线程置为detach状态,也就是说,不能对同一线程调用两次pthread_join,或者如果已经对一个线程调用了pthread_detach就不能在调用pthread_join了。

在这里提一下pthread_detach()函数。它的原型为:int pthread_detach(pthread_t
thread);

返回值:成功返回0,失败返回错误码。

3、有关分离线程

      在任意一个时间点上,线程是可结合(joinable)或者是可分离的(detached)。一个可结合线程是可以被其他

线程收回其资源和杀死的。在被回收之前,他的存储器资源(栈等)是不释放的。相反,一个分离的线程是不能被其

他的线程收回和杀死,它的存储器资源在它终止时由系统自动释放。

    线程的分离状态决定一个线程以什么样的方式来终止自己。默认情况,线程状态被设置为结合的。所以为了避免资

源泄漏等问题,每个可结合线程应该要么被显示的回收,即调用pthread_join;要么通过调用pthread_detach函数

被分离,否则线程的状态类似于进程中的Zombie
Process,会有部分资源没有被回收的。

调用函数pthread_join,当等待线程没有终止时,主线程将处于阻塞状态。如果要避免阻塞,那么在子线程中加入代

码pthread_detach(thread_self());或者在父线程中加入pthread_detach(thread_id)(非阻塞,可立即返回)。这样,

该线程运行结束后会自动释放所有资源。

线程分离的作用:当主线程与新线程无关时,主线程无需等待新线程的结束。
看一下代码:





看运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: