程序 线程 和程序
- 程序: 完成特定功能的一系列有序指令集合
可执行文件
代码段+数据段
- 进程:程序的一次动态执行过程
代码段+数据段+堆栈段+PCB
两者比较
进程 |
程序 |
动态的
数据段
短暂的
永久的
堆栈段+PCB
一个进程只能对应一个程序
一个程序可以对应多个进程
进程 |
线程 |
是资源竞争的基本单位
程序执行最小单位
线程共享进程数据,但也有自己的一部分数据:如下
+ 线程ID
+ 一组寄存器 (IP,SP,状态)
+ 栈
+ errno
+ 信号状态
+ 优先级
fork 和创建新线程的区别
- 当一个进程执行一个fork调用的时候,会创建出进程的一个新的拷贝,新进程将拥有它自己的变量和它自己的PID,这个新的进程运行时间是独立的,它在执行时几乎完全独立于创建它的进程
- 在进程里面创建一个新线程的时候,新的执行线程会拥有自己的堆栈(因此也就有自己的局部变量),但它要和它的创建者共享局部变量、文件描述符、信号处理器和当前的工作目录状态。
线程的优点
线程的缺点
线程调度竞争范围
- 进程竞争范围:各线程在同一进程竞争“被调度的CPU时间”(但不直接和其他进程中的线程竞争)
- 系统竞争范围:线程直接和系统范围内的其他线程竞争
线程模型
+ N:1 用户线程模型
+ 1:1 核心线程模型
+ N:M混合线程模型
pthread_create函数
pthread_join函数
调用pthread_join等待一个线程终止,把线程和UNix进程相比,pthread_create类似于fork,pthread_join 则类似于waitpid
进程 |
线程 |
pid_t
thread_t
fork
pthread_create
waitpid
pthread_join
exit
pthread_exit
僵进程 |
僵线程 |
waitpid
pthread_join
pthread_detach
kill
pthread_cancel
线程结束两种方式:自杀和他杀
自杀
pthread_exit 或是用return
他杀
pthread_cancel
线程的属性
并发级别的设置
- 获取与设置并发级别 : int pthread_setconcurrency(int new_level);
- int pthread_getconcurrency(void);
- 仅在N:M线程模型有效,设置并发级别,给内核一个提示:表示提供给定级别数量的核心线程来映射用户线程是高效的。
线程特定数据
- 在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有
- 但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问
- POSIX线程库通过维护一定的数据结构来解决这个问题,这个就称为(Thread-specific Data 或TSD)。
自旋锁
- 自旋锁类似于互斥锁,它的性能比互斥锁要高
- 自旋锁与互斥锁很重要的一个区别在于,线程在申请自旋锁的时候,线程不会被挂起,他处于忙等待的状态 (适合等待比较小的应用)
读写锁
条件变量
为了解决死锁的问题
phread_cond_wait(&g_cond,&g_mutex);
这个函数内部实现了几步机制:
+ 对g_mutex进行解锁
+ 等待条件,直到线程向它发起通知
+ 重新对g_mutex进行加锁操作
while(1)
{
pthread_mutex_lock(&g_mutex);
while ( nready == 0)
{
printf("%d begin wait condition ...
",num);
pthread_cond_wait(&g_cond,&g_mutex);
}
while(1)
{
pthread_mutex_lock(&g_mutex);
printf("%d begin produce product ...
",num);
++ nready;
pthread_cond_signal(&g_cond);
printf("%d end produce product ...
",num);
printf("%d signal ...
",num);
pthread_mutex_unlock(&g_mutex);
sleep(5);
}
pthread_cond_signal函数
向同一个等待条件的线程发起通知,如果没有任何一个线程处理等待条件的状态,这个通知将被忽略
pthread_cond-broad_cast
向所有等待线程发起通知