文章目录
- 什么是线程???
- 线程和进程的区别和联系
- Linux线程控制
- POSIX线程库
- 创建线程
- 线程等待
- 线程终止
- 线程分离
什么是线程???
- 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
- 一切进程至少都有一个执行线程
- 线程在进程内部运行,本质是在进程地址空间内运行
- 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
- 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
每个进程都有自己独立的进程地址空间和独立的页表
如果我们在创建“进程”时,只创建task_struct,并要求创建出来的task_struct和父task_struct共享进程地址空间和页表
此时我们创建是四个线程:
- 其中每一个线程都是当前进程里面的一个执行流,也就是我们常说的“线程是进程内部的一个执行分支”
- 同时我们也可以看出,线程在进程内部运行,本质就是线程在进程地址空间内运行,也就是几乎都是被所有线程共享的
所以,线程就是一个没有独立的地址空间的task_struct结构,线程的资源是从最开始的主线程,也就是进程来的
CPU调度的是task_struct,并不关心此task_struct是进程的还是线程的,所以线程被称为系统调度的基本单位.而在Linux操作系统下,线程就是轻量化的进程
线程和进程的区别和联系
线程有下面的信息
- 线程ID
- 一组寄存器
- 栈
- errno
- 信号屏蔽字
- 调度优先级
进程的多个线程共享 同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
- 文件描述符表
- 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
- 当前工作目录
- 用户id和组id
进程和线程的关系如下图:
那我们之前说的进程又是什么呢?
Linux线程控制
POSIX线程库
- 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
- 要使用这些函数库,要通过引入头文<pthread.h>
- 链接这些线程函数库时要使用编译器命令的“-lpthread”选项
线程ID的本质是一个地址,pthread库是一个动态库,是个第三方库,这个库会被映射到进程的地址空间的共享区中
创建线程
pthread_create函数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数:
- thread:返回线程ID
- attr:设置线程的属性,attr为NULL表示使用默认属性
- start_routine:是个函数地址,线程启动后要执行的函数
- arg:传给线程启动函数的参数
返回值:
- 成功返回0
- 失败返回错误码
例子:
void *route(void *arg)
{while(1) {printf("I'am a thread \n");sleep(1);}
}
int main()
{pthread_t tid;int ret;if ( (ret=pthread_create(&tid, NULL, rout, NULL)) != 0 ) {fprintf(stderr, "pthread_create : %s\n", strerror(ret));exit(-1);}while(1) {printf("I'am main thread\n");sleep(1);}return 0;
}
线程等待
为什么要线程等待???
已经退出的线程,其空间没有被释放,仍然在进程的地址空间内
创建新的线程不会复用刚才退出线程的地址空间
pthread_cancel函数
int pthread_join(pthread_t thread);
参数:
- thread:线程ID
- value_ptr:它指向一个指针,后者指向线程的返回
返回值:
- 成功返回0
- 失败返回错误码
线程终止
- 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit
- 线程可以调用pthread_ exit终止自己
- 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程
pthread_exit函数
void pthread_exit(void *value_ptr);
参数:
- value_ptr:value_ptr不要指向一个局部变量
返回值:
- 无返回值,跟进程一样,线程结束的时候无法返回到它的调用者
pthread_cancel函数
int pthread_cancel(pthread_t thread);
参数:
- thread:线程ID
返回值:
- 成功返回0
- 失败返回错误码
常使用return终止进程
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>void* Routine(void* arg)
{char* msg = (char*)arg;int count = 0;while (count < 10){printf("I am %s---pid: %d, ppid: %d\n", msg, getpid(), getppid());sleep(1);count++;}return NULL;
}
int main()
{pthread_t tid[5];for (int i = 0; i < 5; i++){char* buffer = (char*)malloc(64);sprintf(buffer, "thread %d", i);pthread_create(&tid[i], NULL, Routine, buffer);printf("%s tid is %lu\n", buffer, tid[i]);}printf("I am main thread...pid: %d, ppid: %d\n", getpid(), getppid());for (int i = 0; i < 10; i++){pthread_join(tid[i], NULL);printf("thread %d[%lu]...quit\n", i, tid[i]);}return 0;
}
线程分离
- 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏
- 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源
pthread_detach函数
int pthread_detach(pthread_t thread);
自己分离
pthread_detach(pthread_self());
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void *thread_run( void * arg )
{pthread_detach(pthread_self());printf("%s\n", (char*)arg);return NULL;
} int main( void )
{pthread_t tid;if ( pthread_create(&tid, NULL, thread_run, "thread1 run...") != 0 ) {printf("create thread error\n");return 1;} int ret = 0;sleep(1);// 让线程先分离,再等待if ( pthread_join(tid, NULL ) == 0 ) {printf("pthread wait success\n");ret = 0;} else {printf("pthread wait failed\n");ret = 1;} return ret;
}