1. 概念
线程是比进程更小的能独立运行的基本单位,线程基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如线程ID,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
一个线程包含以下内容:
- 指向当前被执行指令的指令指针;
- 栈;
- 寄存器值的集合,定义了一部分描述正在执行线程的处理器状态的值;
- 私有的数据区
- 参考
man 7 threads
2. 查看线程
- 命令
No. | 命令 | 含义 |
---|---|---|
1 | ps -T -p <pid> |
-T 开启线程查看 |
2 | top -H -p <pid> |
-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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
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 <pthread.h>
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_t>(func),NULL);
}
id get_id()const{
return _id;
}
void join(){
pthread_join(_id,NULL);
}
void detach(){
pthread_detach(_id);
}
};
};
#endif // __THREAD_H