线程的思维导图:
一,什么是线程
1,线程的概念
线程是“一个进程内部的控制序列”。在linux下,PCB就是线程,是一个轻量级的进程。
2,进程和线程的区别
- 进程是操作系统分配资源的一个基本单位
- 线程是操作系统CPU调度的一个基本单位
- 进程是一个或多个执行流的线程组,即一个进程可拥有多个线程
3 .线程的共享与独享
由于同一进程的多个线程共享同一地址空间,所以代码段,数据段是共享的,如果定义一个函数(存储在代码段),各线程都可以进行调用,如果定义个全局变量(存储在数据段),在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
(1).文件描述符表
(2).每种信号的处理方式(SIG_IGN,SIG_DFL,用户自定义)
(3).当前工作目录
(4).用户id和组id
但有些资源是线程独享的:
(1).线程id
(2).上下文,包括各种寄存器的值,程序计数器和栈指针
(3).栈空间 ,防止调用栈混乱
(4).errno变量
(5).信号屏蔽字
(6).调度优先级
4,线程的优缺点
优点:
- 创建一个新线程的代价要比创一个新进程小得多
- 线程之间的切换需要操作系统做的工作要少很多
- 占用的资源要比进程少很多
- 能充分利用多处理器的可并行数量
- 线程可以同时等待不同的I/O操作
缺点:
- 性能损失:增加了额外的同步和调度开销,而可用的资源不变
- 健壮性降低:因时间分配上的细微偏差 或者因共享了不该共享的变量造成不良影响的可能性是很大的
- 编程难度提高:编写与调试一个多线程程序比单线程程序困难得多
- 缺乏访问控制:在线程中调用exit()函数,将退出进程
二,线程控制
1.线程的创建
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:传给线程启动函数的参数
创建线程代码如下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
void *thr_start(void *arg)
{
while(1) {
printf("thread:calling!!\n");
sleep(1);
}
return NULL;
}
int main()
{
pthread_t tid;
//pthread_create 创建线程,线程入口函数为thr_start
int ret = pthread_create(&tid, NULL, thr_start, NULL);
if (ret != 0) {
printf("pthread_create error!!\n");
return -1;
}
while(1) {
printf("thread:playing!!\n");
sleep(1);
}
return 0;
创建成功就会出现:playing和calling循环打印,按快捷键Ctrl+c终止
2,线程终止
调用exit()的话,主进程会终止。终止线程由三种方法:
pthread_exit函数
void pthread_exit( *value_ptr);
//功能
结束当前线程(结束自己),线程私有资源被释放
//参数
ptr不能是局部变量
ptr可被其他线程通过pthread_join获取
- pthread_cancel函数
//功能:取消一个执行中的线程(终止同一进程中的另一个线程)
//原型
int pthread_cancel(pthread_t thread);
//参数
thread:线程ID
//返回值:成功返回0;失败返回错误码
- 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
测试代码:
void *thr_start(void *arg)
{
pthread_t mid = (pthread_t)arg;
pthread_cancel(mid);//终止主线程
while(1) {
printf("i am child thread!!\n");
sleep(1);
//return NULL;//终止普通线程
//printf("--------exit!!\n");
}
printf("--------exit!!\n");
return NULL;
}
int main()
{
pthread_t tid;
pthread_t mid = pthread_self();
int ret = pthread_create(&tid, NULL, thr_start, (void*)mid);
if (ret != 0) {
printf("pthread_create error\n");
return -1;
}
//pthread_cancel(tid);//终止普通线程
while(1) {
printf("this is main thread!\n");
sleep(1);
//pthread_exit(NULL);//终止主线程
}
return 0;
}
3.线程等待与分离
- 线程等待:线程的等待是以阻塞式等待,线程不等待会产生内存泄露(类似进程的僵尸进程)
//功能:等待线程结束
原型
int pthread_join(pthread_t thread, void **value_ptr);
参数
thread:线程ID
value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码
已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。 创建新的线程不会复用刚才退出线程的地址空间,所以需要线程等待。调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得 到的终止状态是不同的。
- 线程分离:当线程退出时,自动释放线程资源。
int pthread_detach(pthread_t thread);
//可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:
pthread_detach(pthread_self());
线程有两种状态,一种为可结合的,一种为分离的,一般默认下线程是可结合的。
一个线程没有退出,主线程便一直等待其运行结束,对其资源进行回收,这种状态就称为可结合的。
但是一个线程要是被设置为分离的,那么主线程便不会在去等待回收那个线程,这个线程运行结束时会由操作系统对其进行资源回收,而且一个分离线程是不能被其他线程所结束掉的。
注意:当一个进程结束时,其有一个分离的线程还未结束,那么其将会被强制结束,因为线程的资源都是进程分配给它的,而进程结束时其资源会被操作系统回收,此时线程会被结束掉。 当一个线程被设置为分离时,就不能被等待。joinable和分离是冲突的,一个线程不能既是joinable既是分离的。