1. 概念
线程是比进程更小的能独立运行的基本单位,线程基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如线程ID,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
一个线程包含以下内容:
指向当前被执行指令的指令指针;
栈;
寄存器值的集合,定义了一部分描述正在执行线程的处理器状态的值;
私有的数据区
进程与线程静态
进程与线程动态
参考
man 7 threads
2. 查看线程
命令
No.
命令
含义
1
ps -T -p
-T开启线程查看
2
top -H -p
-H开启线程查看
文件
No.
文件
含义
1
/proc/{PID}/task/
线程默认的名字和进程名相同
2
/proc/{PID}/task/{tid}/comm
线程名
3. 操作
No.
操作
函数
1
线程标识
pthread_t pthread_self(void)
2
线程创建
int pthread_create(pthread_t * tidp, pthread_attr_t * attr, void *(*start_rtn)(void), void * arg)
3
子线程终止
void pthread_exit(void* retval)
4
线程合并
int pthread_join(pthread_t tid, void **retval)
5
线程分离
int pthread_detach(pthread_t tid)
6
发送信号
int pthread_kill(pthread_t tid, int sig)
3.1 线程标识
pthread_t pthread_self(void)
返回值
当前线程的线程ID
说明:
线程ID打印使用%lu
pthread_self()不链接库pthread返回值为0.
linux上的线程实现就是在内核支持的基础上以POSIX thread的方式对外封装了接口。
3.2 线程创建
int pthread_create(pthread_t * tidp, pthread_attr_t * attr, void *(*start_rtn)(void), void * arg)
参数
No.
参数
含义
1
tidp
线程ID指针
2
attr
线程属性
示例
两个线程并发执行
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
int i = 0;
for(;i<100;i++){
usleep(500000);
info();
}
}
int main(){
info();
pthread_t tid;
pthread_create(&tid,NULL,method,NULL);
printf("new tid:%lu\n",tid);
int i = 0;
for(;i<100;i++){
sleep(1);
info();
}
sleep(1);
}
模拟抢票
#include
#include
#include
#include
long ticket = 1000000;
void* method(void* arg){
while(ticket > 0){
ticket--;
printf("%lu get a ticket, leave %d\n",pthread_self(),ticket);
//sleep(1);
}
}
int main(){
pthread_t tid;
pthread_setconcurrency(5);
pthread_create(&tid,NULL,method,NULL);
pthread_create(&tid,NULL,method,NULL);
pthread_create(&tid,NULL,method,NULL);
pthread_create(&tid,NULL,method,NULL);
pthread_create(&tid,NULL,method,NULL);
pause();
}
3.3 子线程退出
子线程退出有两种方式
线程处理函数return。
调用子线程终止
void pthread_exit(void* retval)
参数
No.
参数
含义
1
retval
函数的返回指针,只要pthread_join中的第二个参数retval不是NULL,这个值将被传递给retval
用在线程回调函数中,返回线程数据
3.4 线程合并
使用valgrind试试上面的程序是否有内存泄漏?
int pthread_join(pthread_t tid, void **retval)
参数
No.
参数
含义
1
tid
被等待的线程标识符
2
retval
一个用户定义的指针,它可以用来存储被等待线程的返回值
返回值
No.
返回值
含义
1
0
成功
2
非0
错误码
可以由其他线程终止,回收资源
示例
无传参的情况
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
}
int main(){
info();
pthread_t tid;
pthread_create(&tid,NULL,method,NULL);
printf("new tid:%lu\n",tid);
//sleep(1);
pthread_join(tid,NULL);
}
有传参的情况
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
printf("arg:%s",arg);
}
int main(){
info();
pthread_t tid;
char test[]="hello thread";
pthread_create(&tid,NULL,method,test);
printf("new tid:%lu\n",tid);
//sleep(1);
pthread_join(tid,NULL);
}
线程参数
局部变量可以作为线程参数的情况
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
printf("arg:%s",arg);
return "this is return value";
}
int main(){
info();
pthread_t tid;
char test[]="hello thread";
pthread_create(&tid,NULL,method,test);
printf("new tid:%lu\n",tid);
//sleep(1);
void* res = NULL;
pthread_join(tid,&res);
printf("res:%s\n",res);
}
局部变量不能作为线程参数的情况
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
printf("arg:%s",arg);
}
pthread_t create_thread(){
pthread_t tid;
char test[] = "Hello thread";
pthread_create(&tid,NULL,method,(void*)test);
return tid;
}
int main(){
info();
pthread_t tid;
//char test[]="hello thread";
//pthread_create(&tid,NULL,method,test);
tid = create_thread();
printf("new tid:%lu\n",tid);
//sleep(1);
pthread_join(tid,NULL);
}
只读变量可以作为线程参数的情况
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
printf("arg:%s",arg);
}
pthread_t create_thread(){
pthread_t tid;
const char* test = "Hello thread";
pthread_create(&tid,NULL,method,(void*)test);
return tid;
}
int main(){
info();
pthread_t tid;
//char test[]="hello thread";
//pthread_create(&tid,NULL,method,test);
tid = create_thread();
printf("new tid:%lu\n",tid);
//sleep(1);
pthread_join(tid,NULL);
}
堆变量可以作为线程参数的情况
#include
#include
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
printf("arg:%s",arg);
free(arg);
}
pthread_t create_thread(){
pthread_t tid;
char* test = malloc(BUFSIZ);
strcpy(test,"Hello thread");
pthread_create(&tid,NULL,method,(void*)test);
return tid;
}
int main(){
info();
pthread_t tid;
//char test[]="hello thread";
//pthread_create(&tid,NULL,method,test);
tid = create_thread();
printf("new tid:%lu\n",tid);
//sleep(1);
pthread_join(tid,NULL);
}
静态变量可以作为线程参数的情况
#include
#include
#include
#include
int info(){
printf("PID:%d,TID:%lu",getpid(),pthread_self());
char name[16] = {0};
prctl(PR_GET_NAME,name);
printf("TNAME:%s\n",name);
}
void* method(void* arg){
sleep(5);
info();
printf("arg:%s",arg);
}
pthread_t create_thread(){
pthread_t tid;
static char test[] = "Hello thread";
pthread_create(&tid,NULL,method,(void*)test);
return tid;
}
int main(){
info();
pthread_t tid;
//char test[]="hello thread";
//pthread_create(&tid,NULL,method,test);
tid = create_thread();
printf("new tid:%lu\n",tid);
//sleep(1);
pthread_join(tid,NULL);
}
3.5 线程分离
int pthread_detach(pthread_t tid)
参数
No.
参数
含义
1
tid
要释放线程的标识符ID
返回值
No.
返回值
含义
1
0
成功
2
非0
错误码
不能被其他线程终止,存储资源在它终止时由系统自动回收释放
线程分离后不能使用join。
使用valgrind试试pthread_detach()的程序是否有内存泄漏?
4. 进程线程比较
4.1 接口对比
No.
Process Primitive
Thread Primitive
Description
1
getpid()
pthread_self()
获得控制流的id
2
fork()
pthread_create()
创建新的控制流
3
exit()
pthread_exit()
退出已有的控制流
4
waitpid()
pthread_join()
等待控制流并获得结束代码
4.2 特性对比
No.
特性
进程
线程
1
粒度
系统资源分配和调度的基本单位
CPU调度和分派的基本单位
2
资源
有独立的地址空间
共享进程的地址空间
3
效率
上下文切换要较慢
上下文切换较快
4
稳定性
子进程崩溃,不影响父进程与其他子进程
任何一个线程崩溃,整个程序崩溃
5 线程通信
全局变量、动态分配内存和比线程生存周期长的局部变量都可以用于线程通信。
线程通信比进程简单方便。
问题:尽然有了进程,为什么要有线程?
6. C++11 thread类
使用pthread实现简单的thread类。
#ifndef __THREAD_H
#define __THREAD_H
#include
namespace miniSTL{
class thread{
typedef pthread_t id;
typedef void* (*func_t)(void*);
typedef void (*funcv_t)();
id _id; // 线程TID
public:
thread():_id(0){}
thread(func_t func){
pthread_create(&_id,NULL,func,NULL);
}
thread(funcv_t func){
pthread_create(&_id,NULL,reinterpret_cast(func),NULL);
}
id get_id()const{
return _id;
}
void join(){
pthread_join(_id,NULL);
}
void detach(){
pthread_detach(_id);
}
};
};
#endif // __THREAD_H