文章目录
线程概述
线程是操作系统能够进行运算调度的最小单位。它包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程的特点
1)轻型实体
线程中的实体基本上不用有系统资源,只有一点必不可少的、能保证独立运行的资源。
线程的实体包括程序、数据、TCB。线程是动态概念,它的动态特性由线程控制块TCB描述。
TCB包括以下信息:
- 线程状态
- 当线程不运行时,被保存的现场资源。
- 一组执行堆栈
- 存放每一个线程的局部变量主存区。
- 访问同一进程中的主存和其他资源。
【TCB内容:用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈】
2)独立调度和分派的基本单位
在多线程OS中,线程是能够独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(同一进程中)。
3)可并发执行
在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有的线程都能并发允许;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。
4)共享进程资源
在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
5)Linux中不区分进程、线程
6)使用多线程的好处
使用多线程大大提高了任务切换的效率,避免了额外的TLB&cache的刷新
线程的共享资源和私有资源
共享资源 | 私有资源 |
---|---|
可执行的指令 | 线程ID(TID) |
静态数据 | PC(程序计数器)和相关寄存器 |
进程中打开的文件描述符 | 堆栈 |
当前工作目录 | 错误号 |
用户ID | 优先级 |
用户组ID | 执行状态和属性 |
基于linux的线程操作
在Linux线程库pthread中提供了创建线程、回收线程、结束线程的基本操作,同时,线程的同步和互斥机制也是重点。
线程的创建——pthread_create
int pthread_create(pthread_t * thread ,const pthread_attr_t * attr,void * ( * routine)(void * ),void * arg);
- 头文件 #include<pthread.h>
- 成功时返回0,失败时返回错误码
- thread 线程对象
- attr 线程属性,NULL代表默认属性
- routine 线程执行的函数
- arg 传递给routine的参数,参数是void * ,注意传递参数格式
pthread_t pthread_self(void) 查看自己的TID
线程的回收——pthread_join
int pthread_join(pthread_t thread , void ** retval);
- 头文件include<pthread.h>
- 成功时返回0,失败时返回错误码
- thread 要回收的线程对象
- 调用线程阻塞知道thread结束
- *retval 接受线程thread的返回值
线程的结束——pthread_exit
void pthread_exit(void * retval);
- 头文件 #include<pthread.h>
- 结束当前线程
- retval可被其他线程通过pthread_join获取
- 线程私有资源被释放
线程间通信
线程共享同一进程的地址空间,所以线程间通信很容易【通过全局变量交换数据即可】,在多个线程通信时,访问共享数据的时候需要用到同步和互斥机制。
线程通信——同步
同步指的是多个任务按照约定的先后次序相互配合完成一件事情,线程通信的同步机制由信号量来决定线程是继续运行还是阻塞等待
信号量(灯)
信号量代表某一类资源,某值表示系统中该资源的数量.
信号量是一个受保护的变量,只能通过三种操作来访问:
1)初始化
2)P操作(申请资源)【消费者】
3)V操作(释放资源)【生产者】
P操作含义如下:
if(信号量的值大于0)
{
申请资源的任务继续运行;
信号量的值减一;
}
else
{
申请资源的任务阻塞;
}
V操作含义如下:
信号量的值加一;
if(有任务在等待资源)
{
唤醒等待的任务,让其继续运行;
}
Posix 信号量,posix中定义了两类信号量:
- 无名信号量(基于内存的信号量)
- 有名信号量
1)信号量初始化——sem_init
int sem_init(sem_t * sem , int pshared ,unsigned int val);
- 头文件 #include<semaphore.h>
- 成功时返回0,失败时返回EOF
- sem 指向要初始化的信号量对象
- pshared 0代表线程间 1代表进程间
- val 信号量初值
2)信号量的P/V操作
int sem_wait(sem_t * sem); ——P操作
int sem_post(sem_t * sem); ——V操作
- 头文件 #include<semaphore.h>
- 成功时返回0,失败时返回EOF
- sem 指向要P/V操作的信号量对象
线程间通信——互斥
临界资源: 一次只允许一个任务(线程、进程)访问的共享资源
临界区: 临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问。当有线程进入临界区段时,其他线程或是进程必须等待,有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用。
互斥机制通过mutex互斥锁实现,任务访问临界资源前申请锁,访问完后释放锁。
互斥锁的初始化——pthread_mutex_init
在使用互斥锁之前,需要对互斥锁进行初始化。
int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr);
- 头文件 #include<pthread.h>
- 成功时返回0,失败返回错误码
- mutex 指向要初始化的互斥锁对象
- attr 互斥锁属性,NULL代表缺省属性
申请锁——pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t * mutex);
- 头文件 #include<pthread.h>
- 成功时返回0,失败返回错误码
- mutex 指向要上锁的对象
- 如果无法获得锁,任务阻塞
释放锁——pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t * mutex);
- 头文件 #include<pthread.h>
- 成功时返回0,失败返回错误码
- mutex 指向要释放锁的对象
- 执行完临界区要及时释放锁
总结
同步机制与互斥机制还不熟练,需要加深!!!