Linux线程
线程的概念
什么是线程
LWP: light weight process轻量级的进程,本质仍是进程(在Linux环境下)
进程: 独立地址空间,拥有PCB.
线程:也有PCB,但没有独立的地址空间(共享).
区别:在于是否共享地址空间。 独居(进程);合租(线程)。
Linux下:
- 线程:最小的执行单位
- 进程:最小分配资源单位,可看成是只有一个线程的进程。
Linux内核线程实现原理
类Unix系统中,早期是没有“线程”概念的, 80年代才引入,借助进程机制实现出了线程的概念。因此在这类系统中,进程和线程关系密切。
- 轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone.
- 从内核里看进程和线程是一样的,都有各自不同的PCB.
- 进程至少有一个线程.
- 线程可看做奇存器和栈的集合.
- 在linux下,线程最是小的执行单位; 进程是最小的分配资源单位.
查看LWP号 : ps -Lf pid
查看指定线程的lwp号.
线程共享资源
- 文件描述符表
- 每种信号的处理方式 — 不建议信号与线程一起使用 太乱
- 当前工作目录
- 用户ID和组ID
- 内存地址空间 (text, data, bss, heap, 共享库)
线程非共享资源
- 线程 id
- 处理器现场和栈指针 (内核栈)
- 独立的栈空间 (用户空间栈)
- error变量
- 信号屏蔽字
- 调度优先等级
线程优缺点
优点 :
- 提高程序并发性
- 开销小
- 数据通信,共享数据方便
缺点 :
- 库函数不稳定
- 调试编写困难
- 对信号支持不好
优点行对突出, 缺点不是硬伤. Linux下由于实现方法导致进程 线程的差别不是很大.
线程相关函数
pthread_create 创建线程
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
参数
- thread 传出参数线程ID
- attr 线程属性,大多不使用
- 第三参数 函数指针
- arg 线程执行函数的参数
返回值
成功返回0 失败返回error
编译时需要加pthread库
gcc -o $@ $< -g -lpthread
pthread_self 得到线程号
#include <pthread.h>
pthread_t pthread_self(void);
例子
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
void* thr(void *atg)
{
printf("我是线程 我的pid = %d, tid = %lu\n",getpid(),pthread_self());
return NULL;
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,thr,NULL);
printf("我是主线程 pid = %d tid = %lu\n",getpid(),pthread_self());
sleep(1);
return 0;
}
pthread_exit 退出一个线程
#include <pthread.h>
void pthread_exit(void *retval);
例子 :
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void *thr(void * val)
{
printf("我是子线程pid = %d tid = %lu \n",getpid(),pthread_self());
//return NULL;
pthread_exit(NULL);
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,thr,NULL);
printf("我是主线程pid = %d tid = %lu \n",getpid(),pthread_self());
pthread_exit(NULL);
return 0;
}
注意事项:
- 在线程中使用pthread_exit
- 在线程中使用return (主控进程代表退出进程)
- exit代表退出整个进程
pthread_join 获取线程返回值 回收线程
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
参数
- thread 创建时候传入的第一个参数
- retval 传出线程的退出信息
例子 :
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void *thr(void * val)
{
printf("我是子线程 tid = %lu\n",pthread_self());
sleep(4);
printf("我是子线程 tid = %lu\n",pthread_self());
return (void*)20;
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,thr,NULL);
printf("我是主线程 tid = %lu\n",pthread_self());
void *ret;
pthread_join(tid,&ret); // 线程回收, 线程未执行完 阻塞
printf("子线程返回值:%d\n",((int)ret));
pthread_exit(NULL);
return 0;
}
pthread_cancel 杀死线程
#include <pthread.h>
int pthread_cancel(pthread_t thread);
例子 :
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
void *thr(void *val)
{
while(1)
{
printf("我是子线程,tid = %d\n",pthread_self());
sleep(1);
}
pthread_exit(NULL);
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,thr,NULL);
sleep(5);
pthread_cancel(tid);
void *ret;
pthread_join(tid,&ret);
printf("返回值:%d\n",(int)ret);
return 0;
}
pthread_detach 实现线程分离
线程分离状态:指定该状态,线程主动与主控线程断开关系。线程结束后,其退出状态不由其他线程获取,而直接自己自动释放。网络、多线程服务器常用.
进程若有该机制,将不会产生僵尸进程。僵尸进程的产生主要由于进程死后,大部分资源被释放,一点残留资源仍存于系统中,导致内核认为该进程仍存在.
也可使用pthread_create函数参2(线程属性)来设置线程分离.
不需要使用pthread_join回收资源.
#include <pthread.h>
int pthread_detach(pthread_t thread);
例子 :
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
void *thr(void *arg)
{
printf("我是线程 tid = %lu\n",pthread_self());
sleep(4);
printf("我是线程 tid = %lu\n",pthread_self());
//pthread_exit(NULL);
return 0;
}
int main(int argc, char *argv[])
{
pthread_t tid;
pthread_create(&tid,NULL,thr,NULL);
pthread_detach(tid);
sleep(5);
int ret = pthread_join(tid,NULL);
if(ret > 0){
printf("join error:%d, %s\n",ret,strerror(ret));
}
return 0;
}
运行结果 :
我是线程 tid = 139732708251392
我是线程 tid = 139732708251392
join error:22, Invalid argument
pthread_equal 判断两个线程是否相等
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
线程id在进程内是唯一的, 但是在系统中不是唯一的.
查看线程库版本
getconf GNU_LIBPTHREAD_VERSION
创建多少线程合适?
cpu核数*2 + 2