线程的概念
线程(Thread)是进程的一个实体,是CPU调度和分派的基本单位。线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
1、进程是资源管理的最小单位,线程是程序执行的最小单位。
2、在操作系统设计上,从进程演化出线程,最主要的目的就是更好地支持多处理器,并且减小进程上下文切换的开销。
3、线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享其所附属的进程的所有的资源,包括打开的文件、内存页面、信号标识及动态分配的内存等等
线程的优点
1.能够高效地实现多任务操作方式
2.线程间通信非常方便
3.响应度高、资源共享
4.线程的创建和切换的代价小
5.多处理器体系结构的利用:无论系统中有多少个CPU,单线程的进程只能运行在一个CPU上
线程与进程的区别:
1.进程是程序的一次运行活动,进程是系统进行资源分配的独立单位。 线程是进程的一个实体,是CPU调度的基本单位,它是比进程更小的能独立运行的基本单位
2.一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,多线程程序的并发性高。
3.进程在执行过程中拥有独立的内存单元,而多个线程是共享内存,从而极大地提高了程序的运行效率。 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
线程和进程的关系
线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一物理内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。
线程的基本操作
创建线程
等待线程结束
分离线程
删除线程键
设置线程数据
获取线程数据
获取线程标识符
比较线程
一次执行
出让执行权
修改优先级
获取优先级
发送信号
设置线程掩码
终止线程
退出线程
允许/禁止推出线程
设置退出类型
创建退出点
压入善后处理函数
弹出善后处理函数
函数:
pthread_self--获取当前线程id
头文件:pthread.h
原型:pthread_t pthread_self(void);
参数:无 返回值:
返回当前线程的id
创建进程
头文件:pthread.h
原型:int pthread_create ( pthread_t *tid, const pthread_attr_t *tattr, void *(*start_routine)(void*), void *arg );
参数:tid用于保存新建线程的ID,
tattr 是初始化线程所需的属性,值赋为 NULL 即缺省。
start_routine 是线程入口函数的起始地址。当 start_routine 返回时,相应线程就结束。
arg是线程运行函数时所带的参数
返回值:成功返回0,否则返回错误码
注意:创建子线程时,传给子线程的输入参数最好是 malloc() 返回的指针(这样的指针指向进程堆中的存储空间)或指向全局变量的指针,而不要是指向局部变量的指针。因为当子线程访问输入参数时,创建子线程的函数可能已结束,局部变量就不存了
实例:
注意: 1、编译方法:gcc -lpthread thread.c -o thread 因为pthread库不在linux系统库,所以编译的时候要-lpthread,否则错误 2、多线程同时运行时,运行顺序是不确定的,因为线程的运行取决于线程争夺CPU资源的结果
3、所创建的线程属性值为NULL则创建默认属性的线程 缺省的线程的属性:
非绑定
未分离
一个缺省大小的堆栈
具有和父线程一样的优先级
用 phread_attr_init() 创建一个缺省的属性对象,
等待线程结束 -pthread_join
头文件: #include<pthread.h>
函数原型:int pthread_join( pthread_t tid, void **status );
函数功能:pthread_join() 会阻塞调用它的线程,直到参数 tid 指定的线程结束。 tid 指定的线程必须在当前进程中,且必须是非分离的。 status 接收指定线程终止时的返回状态码。 不能有多个线程等待同一个线程终止,否则返回错误码 ESRCH ,当 pthread_join() 返回时,终止线程占用的堆栈等资源已被回收。
返回值:成功返回 0
线程结束的途径:
一个线程的结束有两种途径:
1、线程函数运行结束,则调用它的线程也就结束;
2、通过函数pthread_exit()来结束线程的运行
头文件:pthread.h
原型:void pthread_exit(void * value_ptr);
参数:value_ptr 线程返回值指针
线程取消 -- pthread_cancel
头文件:pthread.h
原型:int pthread_cancel( pthread_t thread );
参数:thread 为所要取消的线程
返回值:成功返回0,非0表示不成功
功能补充:一线程调用pthread_cancel函数向另一线程的发送一个取消信号, pthread_cancel并不能结束其它进程,一般使用了pthread_cancel之后再调用pthread_join
pthread_exit与pthread_cancel的区别:
pthread_exit用于当前线程退出,而pthread_cancel是当前线程取消另一线程的执行
线程同步-互斥锁
pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁(互斥变量地址),即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。
函数pthread_mutex_init()用来生成一个互斥锁:
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
1、初始化互斥锁:pthread_mutex_init()
头文件:pthread.h 函数
原型: int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
函数参数: mutex:互斥锁。
Mutexattr :锁的类型
PTHREAD_MUTEX_INITIALIZER 创建快速互斥锁。
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 创建递归互斥锁。 PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP 创建检错互斥锁。
函数返回值:成功:0;出错:-1
2、锁住互斥:pthread_mutex_lock()或者 pthread_mutex_trylock()
头文件:pthread.h
原型: int pthread_mutex_lock(pthread_mutex_t *mutex);
参数:mutex互斥id
返回值:成功返回0,不成功返回-1
功能补充:如果指定的互斥id已经被锁住那么呼叫线程在互斥id完全解锁前将一直处于挂起状态,否则将锁住互斥体
头文件:pthread.h
原型:int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数:mutex互斥id
返回值:成功返回0,不成功返回-1,如果已经被其线程上锁则返回EBUSY(在errno.h中);
功能:如果指定的互斥id已经被锁住那么将直接返回一个错误,通过判断此错误来进行不同的处理,测试完之后会加锁
4、互斥锁解锁:pthread_mutex_unlock()
头文件:pthread.h
原型: int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:mutex互斥id
返回值:成功返回0,不成功返回-1
5、销毁互斥锁:pthread_mutex_destroy()
头文件:pthread.h
原型: int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:mutex互斥id 返回值:成功返回0,不成功返回-1、
互斥锁实例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int lock_var;
time_t end_time;
void pthread1(void *arg);
void pthread2(void *arg);
void pthread1(void *arg)
{
int i;
while(time(NULL) < end_time){
if(pthread_mutex_lock(&mutex)!=0) /*互斥锁上锁*/
perror("pthread_mutex_lock");
else printf("pthread1 lock the variable\n");
for(i=0;i<5;i++){
sleep(1);
lock_var++;
}
if(pthread_mutex_unlock(&mutex)!=0) /*互斥锁解锁*/
perror("pthread_mutex_unlock");
else printf("pthread1 unlock the variable\n");
sleep(1);
}
}
void pthread2(void *arg) {
int nolock=0,ret;
while(time(NULL) < end_time){
ret=pthread_mutex_trylock(&mutex); /*测试互斥锁*/
if(ret==EBUSY) printf("variable is locked pthread1\n");
else{ if(ret!=0){ perror("pthread_mutex_trylock"); exit(1);
} else printf("pthread2 got lock.variable is%d\n",lock_var);
if(pthread_mutex_unlock(&mutex)!=0)/*互斥锁解锁*/
perror("pthread_mutex_unlock");
else printf("pthread2 unlock the variable\n");
}
sleep(3);
}
}
int main(){
pthread_t id1,id2,mon_th_id;
int ret; end_time = time(NULL)+10;
pthread_mutex_init(&mutex,NULL); /*互斥锁初始化*/
ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);/*创建线程*/
if(ret!=0)
perror("pthread cread1");
ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);/*创建线程*/
if(ret!=0)
perror("pthread cread2"); pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);
exit(0);
}
死锁
死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁
多个线程间在同一互斥资源上产生了循环等待
解决办法:使用函数pthread_mutex_trylock(),它是函数pthread_mutex_lock()的非阻塞函数 :
#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);
成功返回0;
线程同步-条件变量
互斥锁的缺点是它只有两种状态:锁定和非锁定。
条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。
条件变量通常和互斥锁一起使用。
条件变量被用来阻塞一个线程,当条件不满足时,线程通常先解开相应的互斥锁进入阻塞状态,等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。
条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出。
pthread_cond_init --初始化条件变量
头文件:pthread.h
原型:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t*cond_attr);
参数:使用 cond_attr 指定的属性初始化条件变量 cond,当 cond_attr 为 NULL 时,使用缺省的属性。LinuxThreads 实现条件变量不支持属性,因此 cond_attr 参数实际被忽略
返回值: 在执行成功时,所有条件变量函数都返回 0,错误时返回非零的错误代码
注意:pthread_cond_t 类型的变量也可以用 PTHREAD_COND_INITIALIZER 常量进行静态初始化。
pthread_conf_signal --启动线程
头文件:pthread.h
原型:int pthread_cond_signal(pthread_cond_t *cond);
参数:cond为条件变量,初始化了的条件变量
返回值:在执行成功时,所有条件变量函数都返回 0,错误时返回非零的错误代码
功能补充:使在条件变量上等待的线程中的一个线程重新开始。如果没有等待的线程,则什么也不做。如果有多个线程在等待该条件,只有一个能重启动,但不能指定哪一个
pthread_conf_broadcast --重启动等待该条件变量的所有线程
头文件:pthread.h
原型:int pthread_cond_broadcast(pthread_cond_t *cond);
参数:cond为条件变量,初始化了的条件变量
返回值:在执行成功时,所有条件变量函数都返回 0,错误时返回非零的错误代码 功能补充: 重启动等待该条件变量的所有线程。如果没有等待的线程,则什么也不做。
pthread_conf_wait --自动解锁互斥量并等待条件变量触发
原型: int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数:自动解锁互斥量(如同执行了 pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用 CPU 时间,直到条件变量被触发。在调用 pthread_cond_wait 之前,应用程序必须加锁互斥量。pthread_cond_wait 函数返回前,自动重新对互斥量加锁(如同执行了 pthread_lock_mutex)。
返回值:在执行成功时,所有条件变量函数都返回 0,错误时返回非零的错误代码
pthread_conf_des销毁一个条件变量
原型:int pthread_cond_destroy(pthread_cond_t *cond);
参数:cond为条件变量
返回值:在执行成功时,所有条件变量函数都返回 0,错误时返回非零的错误代码
功能补充:pthread_cond_destroy 销毁一个条件变量,释放它拥有的资源。进入 pthread_cond_destroy 之前,必须没有在该条件变量上等待的线程。在 LinuxThreads 的实现中,条件变量不联结资源,除检查有没有等待的线程外,pthread_cond_destroy 实际上什么也不做。
注意:条件变量函数不是异步信号安全的,不应当在信号处理程序中进行调用。特别要注意,如果在信号处理程序中调用 pthread_cond_signal 或 pthread_cond_boardcast 函数,可能导致调用线程死锁。