Linux多线程

原文链接: https://www.cnblogs.com/yinbiao/p/11177748.html

 

进程的概念:进程是对运行时程序的封装,是系统进行资源调度和分配的基本单位,实现了操作系统的并发。

线程的概念:线程时进程的子任务,是cpu调度和分派的基本单位。线程时操作系统可以以识别的最小的执行和调度单位,每一个线程都独占一个虚拟处理器,独自的寄存器组、指令计数器和处理器状态。每个线程完成不同的任务,但是共享同一地址空间,打开文件队列和其他内核资源。

线程与进程的区别:

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。

(2)进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。)

(3)进程是资源分配的最小单位,线程是CPU调度的最小单位

(4)系统开销: 由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/o设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。类似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。可见,进程切换的开销也远大于线程切换的开销。

(6)进程间不会相互影响 ;线程一个线程挂掉将导致整个进程挂掉

一、线程创建与结束

相关函数:

1)int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void*),void *arg)

线程创建函数

参数1:*thread,需要创建的线程ID指针

参数2:*attr,用来设置线程属性

参数3:void*,线程运行函数的起始地址,页就是告诉线程你的线程运行函数是哪一个

参数4:*arg,线程运行函数的参数

函数的返回值int是函数是否运行成功的结果,当返回值为0表示运行成功,-1表示运行失败。当线程运行函数的参数不止一个时,需要将这些参数封装成一个结构体传进去。

2)int pthread_join(pthread_t thread,void **retval)

调用线程等待thread线程运行结束,并且获得thread线程的返回值

参数1:thread,被等待线程的线程ID

参数2:用来存储thread线程的返回值

该函数一般是主线程调用,用来等待子线程运行完毕,函数的返回值int是函数是否运行成功的结果,当返回值为0表示运行成功,-1表示运行失败

3)void pthread_exit(void *retval)

结束当前线程,并返回一个返回值

参数1:*retval,线程结束的返回值

一般pthread_exit和pthread_join配套使用,获得子线程的返回值

#include 
#include
using namespace std;

void* say_hello(void* args)
{
    cout<<"this is a hello from thread"<Linux多线程_第1张图片 

 

二、线程的属性

在创建进程的时候,第二个参数是线程的属性。

typedef struct  
{  
    int                   etachstate;      //线程的分离状态  
    int                   schedpolicy;     //线程调度策略  
    structsched_param     schedparam;      //线程的调度参数  
    int                   inheritsched;    //线程的继承性  
    int                   scope;           //线程的作用域  
    size_t                guardsize;       //线程栈末尾的警戒缓冲区大小  
    int                   stackaddr_set;   //线程的栈设置  
    void*                 stackaddr;       //线程栈的位置  
    size_t                stacksize;       //线程栈的大小  
}pthread_attr_t;

(1)线程的分离状态:线程的分离状态决定一个线程以什么样的方式终止自己。

a.非分离状态:线程的默认属性时非分离状态。父线程等待子线程结束,只有当pthread_join函数返回时,子线程才算终止,才能释放自己占用的系统资源。

b.分离状态:分离线程没有被其他线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源,可以根据自己的需要,选择适当的分离状态。

如何令线程分离?

(1)在创建线程的时候将线程设置为分离线程。

pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)

第二个参数可选为:PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD_CREATE_JOINABLE(非分离线程)

注意:

当将线程设置为分离转台的时候,这个线程可能运行非常快。在pthread_create函数返回之前就终止了。它终止之后线程号和资源可能交给其他线程使用。这个时候pthread_create返回了错误的线程号。这种时候可以在线程运行的函数中调用pthread_cond_timewait函数,令线程多睡眠几秒,留出足够的时间让pthread_create返回,设置一段等待时间。

(2)方法2:在需要分离的线程的运行函数中掉用pthread_detached函数

线程可以通过pthread_self()为参数,利用pthread_detach来分离他们自己。

pthread_detach(pthread_self())

 

2)线程的栈地址

当进程栈地址空间不够时,,指定新建线程使用malloc分配的空间作为自己的栈空间,通过pthread_attr_setstackaddr和pthread_attr_getstackaddr两个函数分别设置和获取线程的栈地址,传给pthread_addr_setstackaddr函数的地址是缓冲区的低地址(不一定是栈的开始地址,栈可能从高地址往低地址增长)

 

3)线程的栈大小

1.当系统中有很多线程时,可能需要减小每个线程的栈空间默认大小,防止进程的地址空间不够用

2.当线程调用的函数会分配很大的局部变量或者函数调用层次很深时,可能需要增加线程默认栈的大小

3.函数pthread_attr_getstacksize和 pthread_attr_setstacksize提供设置。

 

4)线程的栈保护区大小

1.在线程栈的栈顶预留一段空间,防止栈溢出

2.当栈指针进入这段保护区时,系统会发出错误,通常是发送信号给线程

3.该属性的默认值是PAGESIZE大小,该属性被设置时,系统会自动将该属性大小补齐为页大小的整数倍

4.当栈改变地址属性时,栈保护区大小通常清零

 

5)线程的优先级

1.新线程不继承父进程的优先级,新线程的优先级默认为0

2.新线程使用SCHED_OTHER调度策略,即:线程一旦开始运行,直到被抢占或者直到线程阻塞或停止为止

 

6)线程的争用范围

1)PTHREAD_SCOPE_SYSTEM:此线程与系统中的所有线程进行竞争

2)PTHREAD_SCOPE_PROCESS:此线程与进程中的其他线程进行竞争

具有不停争用范围的线程可以在同一个线程甚至同一个进程中共存,进程范围只允许这种与同一进程中的其他线程争用资源,而系统范围则允许此类线程与系统中其他所有线程争用资源

你可能感兴趣的:(Linux多线程)