多线程编程

线程

概念:每个正在系统上运行的程序都是一个进程。每个进程包含一个到多个线程。进程也可能是整个程序或部分程序的动态执行,线程是一组指令的集合或者是程序的特殊段,它可以在程序里单独执行。线程也可以理解为代码运行的上下文。
线程是程序一个单一的顺序控制流程,在单个进程中同时运行多个线程完成不同的工作,称为多线程。
进程和线程的区别:
                         1、子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间。
                         2、进程和线程的操作系统资源管理方式不同。
                         3、操作系统将进程看成独立的应用,实现进调度管理和资源分分配,而操作系统并没有把线程看成独立的应用。
进程与线程的关系:
                         1、一个线程可以创建和撤销另一个线程,同一个进程中多个线程可以并发执行。
                         2、线程可以和进进程中的其他进程共享数据,但每个线程拥有自己的栈空间,拥有独立的执行序列。

每个进程至少有一个主执行线程,它无需用户自己主动创建,由操作系统自动创建,主线程终止,则进程也随之终止。

各线程共享以下进程的资源和环境:
                                                  1、文件描述表
                                                  2、每种信号的处理方式
                                                  3、当前工作目录
                                                  4、用户id和组id
各线程独有:
               1、线程id
               2、上下文(寄存器的值、程序计数器和栈指针)
               3、 栈空间
               4、errno变量
               5、信号屏蔽字
               6、调度优先级

线程控制

创建线程

#include <pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
参数说明:
          thread:指向pthread_create类型的指针,用于引用新创建的线程。
          attr:设置线程属性,一般不设置,简单的设置为NULL。
           *(*start_routine)(void *):传递新线程所要执行的函数地址。
          arg:新线程所要执行函数的参数。
返回值:创建成功返回0,失败返回错误码。
终止线程:
               1、从线程函数return,但主线程不能这样,主线程return相当于调用exit。
               2、一个线程调用pthread_cancel终止同一进程中的另一线程。
               3、线程调用pthread_exit终止自己。
如果任意一个下线程调用了exit或者_exit,则这个进程的所有线程都终止。
pthread_exit和return返回的指针所指向的内存单元必须是全局的或者是malloc分配的,不能在线程栈上分配内存单元,因为当其他线程得到这个返回指针时线程函数已经退出了。
线程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
thread线程以不同的方法终止,通过pthread_join得到的种终止状态是不同的。总结如下(value_ptr:retval指针所指向的指针):
1、如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。
2、如果thread线程被别的线程调用pthread_cancel异常终止,value_ptr所指向的单元存放的是常数PTHREAD_CANCELED。
3、如果thread线程是自己pthread_exit终止的,value_ptr所指向的单元是传给pthread_exit的参数。
分离线程
线程是可结合的或者是可分离的,一个可结合的线程能够被其他线程收回其资源和杀死,咋被其他线程 回收之前,它的存储器资源(比如栈)是不释放的。一个分离的线程是不能被其他线程回收或者杀死的,他的存储器资源在它终止时由系统自动释放。
默认情况下,线程被创建成可结合的,为了避免存储器泄漏,每个可结合线程都应该要么显示的回收即调用pthread_join;要么调用pthread_detch函数被分离。
由于调用pthread_join后,如果该线程没有运行结束,则调用会被阻塞。
线程的同步和互斥
原子操作:指不会被 线程调度 机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
多线程的并发运行:指从宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务。

两个线程同步的基本方法:互斥量和信号量

互斥量
一个线程想要调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调用 pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用 pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。
Mutex变量的值为1表示互斥锁空闲,这时某个进程调用lock可以获得锁,而Mutex的值为0表示互斥锁已经被某个线程获得,其它线程再调用lock只能挂起等待。

死锁
两种典型的死锁:
1、如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程就会挂起等待别的线程释放锁,然而锁正是被自己占用着,该线程又被挂起而没有机会释放锁,则就永远处于挂起等待状态了。
2、线程A获得了锁1,线程B获得了锁2,这,时线程A调用lock试图获得锁2,结果需要挂起等待线程B释放锁2,恰好,线程B也调用lock试图获得锁1,结果需要挂起等待线程A释放锁1,于是线程A和线程B就永远挂起等待了。
如果所有线程在需要多个锁时,都按着相同的先顺序(常见的是Mutex变量的地址顺序)
信号量
1.信号量创建
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数说明:
sem:信号量对象。
pshared:控制信号量的类型,0表示这个信号量用于同一进程的线程间同步,否则,这个信号量就可以在多个进程之间共享。
value:信号量的初始值。
调用sem_wait()可以获得资源(P操作),使semaphore的值减1,如果调用的sem_wait()时,semaphore的值已经是0,则挂起等待(如果不希望等待可以调用sem_trywait()),调用sem_post()可以释放资源(V操作),使semaphore的值加1,同时唤醒挂起等待的线程。

读写锁
读写锁是一个特殊的自旋锁,它把对共享资源的访问分为读者和写者,读者只能对共享资源读操作,写者只能对共享资源写操作,它允许同时有多个读者访问共享资源,一个读写锁同时只能有一个写者或者多个读者。不能同时既有读者和写者。 
自旋锁:为保护共享资源的提出的一种锁机制。其实,自旋锁和互斥锁相似,都是对某项资源的互斥访问,在任何时刻,只能有一个执行单元获得锁,对于互斥锁,如果锁已经被占用,则另一个申请者会挂起等待,对于自旋锁,如果锁已经被占用,则另一个申请者不会挂起等待,它一直循环在哪里看该自旋锁是不是已经被释放。
文件锁: 确保 任何给定的文件执行更新过程的系列化,一个进程修改文件,如果另一个进程访问同一个文件则该进程必须等待文件修改完毕后成才可以访问。
大内核锁: 一旦某个内核路径获取了这把锁,那么其他所有的内核路径都不能再获取到这把锁。












       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值