进程
- 进程有独立的地址空间
- Linux为每个进程创建task_struct
- 每个进程都参与内核调度,互不影响
线程
- 进程在切换时系统开销大
- 很多操作系统引入了轻量级进程LWP
- 同一进程中的线程共享相同地址空间
- Linux不区分进程、线程
线程特点
- 通常线程指的是共享相同地址空间的多个任务
- 大大提高了任务切换的效率
- 避免了额外的TLB & cache的刷新
线程共享资源
一个进程中的多个线程共享以下资源:
- 可执行的指令
- 静态数据
- 进程中打开的文件描述符
- 当前工作目录
- 用户ID
- 用户组ID
线程私有资源
每个线程私有的资源包括:
- 线程ID (TID)
- PC(程序计数器)和相关寄存器
- 堆栈
- 错误号 (errno)
- 优先级
- 执行状态和属性
Linux线程库
pthread线程库中提供了如下基本操作
- 创建线程
- 回收线程
- 结束线程
同步和互斥机制
- 信号量
- 互斥锁
线程创建 – pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*routine)(void *), void *arg);
- 成功返回0,失败时返回错误码
- thread 线程对象
- attr 线程属性,NULL代表默认属性
- routine 线程执行的函数
- arg 传递给routine的参数 ,参数是void * ,注意传递参数格式
线程结束 – pthread_exit
#include <pthread.h>
void pthread_exit(void *retval);
- 结束当前线程
- retval可被其他线程通过pthread_join获取
- 线程私有资源被释放
线程查看tid函数
pthread_t pthread_self(void) 查看自己的TID
#include <pthread.h>
pthread_t pthread_self(void);
线程创建与结束代码范例
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *testThread(void *arg){
printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
// return NULL;
printf("input arg=%d\n",(int)arg);//可以打印出来
pthread_exit(NULL);
printf("after pthread exit\n");//打印不出来,线程已经退出
}
int main(){
pthread_t tid;
int ret;
int arg = 5;
ret = pthread_create(&tid,NULL,testThread,(void *)arg);
printf("This is main thread,tid=%lu\n",tid);
sleep(1);
}
注意事项:
- 1.主进程的退出,它创建的线程也会退出。
- 2.线程创建需要时间,如果主进程马上退出,那线程不能得到执行
线程间参数传递
编译错误:
createP_t.c:8:34: warning: dereferencing ‘void *’ pointer printf("input arg=%d\n",(int)*arg);
createP_t.c:8:5: error: invalid use of void expression printf("input arg=%d\n",(int)*arg);
错误原因是void *类型指针不能直接用*取值(*arg),因为编译不知道数据类型。
解决方法:转换为指定的指针类型后再用*取值 比如:*(int *)arg
- 通过地址传递参数,注意类型的转换
- 值传递,这时候编译器会告警,需要程序员自己保证数据长度正确
线程回收 – pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
对于一个默认属性的线程 A 来说,线程占用的资源并不会因为执行结束而得到释放
- 成功返回0,失败时返回错误码
- thread 要回收的线程对象
- 调用线程阻塞直到thread结束
- *retval 接收线程thread的返回值
线程回收代码实例
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
printf("This is child thread\n");
sleep(25);
pthread_exit("thread return");
}
int main(){
pthread_t tid[100];
void *retv;
int i;
for(i=0;i<100;i++){
pthread_create(&tid[i],NULL,func,NULL);
}
for(i=0;i<100;i++){
pthread_join(tid[i],&retv);//阻塞,如果线程没有退出,则等待
printf("thread ret=%s\n",(char*)retv);//返回的字符串是pthread_exit函数的参数
}
while(1){
sleep(1);
}
}
线程回收的其他方式,使用线程的分离:
两种方式:
1 使用pthread_detach
2 创建线程时候设置为分离属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
线程分离pthead_detach
int pthread_detach(pthread_t thread);
成功:0;失败:错误号
指定该状态,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程)
线程分离代码实例
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
pthread_detach(pthread_self());//方法二
printf("This is child thread\n");
sleep(25);
pthread_exit("thread return");
}
int main(){
pthread_t tid[100];
void *retv;
int i;
for(i=0;i<100;i++){
pthread_create(&tid[i],NULL,func,NULL);
// pthread_detach(tid);//方法一
}
while(1){
sleep(1);
}
}
设置线程属性为分离
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
设置线程属性为分离实例
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
printf("This is child thread\n");
sleep(25);
pthread_exit("thread return");
}
int main(){
pthread_t tid[100];
void *retv;
int i;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
for(i=0;i<100;i++){
pthread_create(&tid[i],&attr,func,NULL);
}
while(1){
sleep(1);
}
}
取消一个线程
int pthread_cancel(pthread_t thread); 杀死一个线程
void pthread_testcancel(void);
int pthread_setcancelstate(int state, int *oldstate);
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
int pthread_setcanceltype(int type, int *oldtype);
PTHREAD_CANCEL_DEFERRED
PTHREAD_CANCEL_ASYNCHRONOUS
线程的清理
void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)