线程&线程控制

线程基本概念:

1 线程

  (1)概念:linux下没有真正的线程,所谓的线程都是通过进程的pcb模拟的,因此linux下的线程也称为“轻量级进程”,之前我们所说的进程现在看来,可以理解为:只有一个线程的线程组。也可以将线程理解为:在一个程序里的一个执行路线,更准确的来说,线程是一个进程内部的控制序列

  (2)进程:至少有一个线程的线程组

  (3)linux下的pcb是task_struct,task_struct中的tgid是线程组id,也等于第一个进程的pid,如果再创建新的线程,这些线程的tgid会相同

2 线程和进程

  (1)进程是资源竞争的基本单位

  (2)线程是程序执行的最小单位

  (3)线程共享进程的数据,比如进程的正文段,数据段,文件描述符(代表一个线程打开文件,另一个线程也可以拿着文件描述在安全的前提下进行操作),信号的处理方式,当前工作目录,用户id,组id。但线程也有自己独有的资源:栈,上下文数据,errno全局变量,信号屏蔽字,调度优先级等等。(注:这些独有的数据都存放在虚拟地址空间的共享区)

  (4)进程和线程的关系如下图:

线程&线程控制

3 线程的优点:

  (1)创建一个线程的代价要比创建一个线程的代价小的多

  (2)与进程之间的切换先比而言,线程之间的切换需要操作系统做的工作很少

  (3)线程占有的资源要比进程小很多

  (4)能充分利用多处理器的可并行数量

  (5)在等待慢速I/O操作结束的同时,程序可执行其他的计算任务

  (6)线程多用于cpu密集型与I/O密集型程序

4 线程的缺点:

  (1)性能损失:在有的情况下会增加额外的调度与同步开销(比如有的操作无法与其他线程共享处理器,当这种操作的数量大于处理器的数量时,会产生这种情况)

  (2)健壮性降低:线程切换之间缺乏安全保护,需要额外的操作保证线程对共享资源的安全访问

  (3)缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响

  (4)编程难度较高

线程控制

注意:我们所使用的是大佬们封装的一套接口,因此所使用的都是库函数,所以也说这套接口创建的线程是用户态线程,并且这个用户态线程在操作系统中对应了一个轻量级进程

1 线程创建

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

  参数:第一个参数返回线程id,第二个参数表示要设置线程的属性,第三个参数表示线程启动后要执行的函数,第四个参数表示创个线程启动函数的参数

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

2 线程终止

  void pthread_exit(void *value_ptr);

  参数:输出型参数,带出线程退出时的返回值,不能讲局部变量的地址传给它;

  无返回值

  int pthread_cancel(pthread_t thread);

  功能:同一进程中的其他线程,可以取消另一个线程

  参数:线程id

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

3 线程等待与分离

  (1)需要线程等待的原因:已经退出的线程,其空间没有释放,仍然在进程地址空间中,创建新的线程不会覆盖退出线程的空间,因此为了避免内存泄漏,需要线程等待,获取线程的退出状态,并且允许系统回收资源

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

    参数:第一个参数:表示需要等待线程的线程id,第二个参数是输出型参数,指向一个指针,表示线程的退出状态返回值,传NULL表示不关心返回状态

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

  注意:只有线程处于joinable状态,这个线程才能被等待,此状态是线程的默认属性;除此之外,还有detach状态,此状态表示线程退出后直接释放资源,线程无法被等待。线程的这两个属性是互斥的,二者只能选其一

  (3)分离线程:int pthread_detach(pthread_t thread)

    int pthread_detach(pthread_self())

线程的同步与互斥

  大部分情况下,线程使用的数据都是局部变量,变量的地址空间在线程地址空间中,这种情况下,变量归单个线程,其他线程无法获得这种变量;但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互;多个线程并发的操作共享变量,会引发一些问题;因此需要通过同步与互斥实现一定的保护机制

1 互斥量

  互斥:同一时间理解资源的唯一访问性

  互斥锁是原子操作 

  (1)定义胡互斥锁变量:pthread_mutex_t mutex

  (2)初始化变量:pthread_mutex_init(&mutex)

  (3)临界资源加锁/解锁

    pthead_mutex_lock(&mutex)

    pthread_mutex_unlock(&mutex)

    pthread_mutex_trylock(&mutex):非阻塞

    pthread_mutex_timelock(&mutex)

  (4)销毁互斥锁:

    pthread_mutex_destory(&mutex)

2 条件变量

  当一个线程互斥的访问某个变量时,他可能发现在其他线程改变状态之前什么也做不了:例如当一个线程访问队列时,发现队列为空,它只能等待其他线程将一个节点添加到队列中,这种情况下需要条件变量

  (1)初始化

    pthread_cond_init(&cond)

  (2)销毁

    pthread_cond_destory(&cond)

  (3)等待条件满足

    pthread_cond_wait(&cond,&mutex)

    注意:为了保护条件的修改,所以我们使用了互斥锁进行保护,但是如果加锁之后条件不满足而陷入休眠,则造成死锁,因此在休眠之前先解锁,但是解锁和休眠应该是原子操作,,否则也可能会诱发死锁的情况出现

  (4)唤醒等待

    pthread_cond_signal(&cond)   只通知一个

    pthread_cond_broadcast(&cond)  唤醒所有等待的线程

3 死锁产生的必要条件

  (1)互斥条件

  (2)不可剥夺条件

  (3)请求与保持条件

  (4)环路等待条件

4 预防死锁

  破坏其中的一个条件

  银行家算法

  死锁检测算法

上一篇:【PHP篇】运算及流程控制


下一篇:069 Hue协作框架