Linux 线程简析
来源:互联网 发布:flac播放软件 编辑:程序博客网 时间:2024/06/07 17:22
线程概念
- LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下)
- 进程:独立地址空间,拥有PCB
- 线程:也有PCB,但没有独立的地址空间(共享)
- 区别:在于是否共享地址空间。 独居(进程);合租(线程)。
- Linux下——
线程:最小的执行单位
进程:最小分配资源单位,可看成是只有一个线程的进程。
对于进程来说,相同的地址(同一个虚拟地址)在不同的进程中,反复使用而不冲突。原因是他们虽虚拟址一样,但,页目录、页表、物理页面各不相同。相同的虚拟址,映射到不同的物理页面内存单元,最终访问不同的物理页面。 但!线程不同!两个线程具有各自独立的PCB,但共享同一个页目录,也就共享同一个页表和物理页面。所以两个PCB共享一个地址空间。 实际上,无论是创建进程的fork,还是创建线程的pthread_create,底层实现都是调用同一个内核函数clone。 如果复制对方的地址空间,那么就产出一个“进程”;如果共享对方的地址空间,就产生一个“线程”。 因此:Linux内核是不区分进程和线程的。只在用户层面上进行区分。所以,线程所有操作函数 pthread_* 是库函数,而非系统调用。
线程共享资源:
- 文件描述符表
- 每种信号的处理方式
- 当前工作目录
- 用户ID和组ID
- 内存地址空间 (.text/.data/.bss/heap/共享库)
线程非共享资源:
- 线程id
- 处理器现场和栈指针(内核栈)
- 独立的栈空间(用户空间栈)
- errno变量
- 信号屏蔽字
- 调度优先级
线程优缺点:
优点: 1. 提高程序并发性 2. 开销小 3. 数据通信、共享数据方便
缺点: 1. 库函数,不稳定 2. 调试、编写困难、gdb不支持 3. 对信号支持不好
线程函数
pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
- 参数1:传出参数,保存系统为我们分配好的线程ID
- 参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
- 参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
- 参数4:线程主函数执行期间所使用的参数。
可以通过调用pthread_self(void)来获取当前线程ID
pthread_exit
void pthread_exit(void *retval);
参数retval传入要返回的数据,通常传NULL。
但要注意不能传在线程栈上分配的内存的数据
pthread_join
int pthread_join(pthread_t thread, void **retval); //成功0 失败 errno
回收线程
thread线程ID,retval存储线程结束状态
线程通过return或者pthread_exit返回的数据会通过retval接受到,如若对线程终止状态不感兴趣可以传递NULL给retval
pthread_detach
int pthread_detach(pthread_t thread); //成功 0 失败 errno
指定该状态,线程主动与主控线程断开关系,线程结束后自动释放。
调用该函数将不能通过pthread_join函数获取到线程退出状态,不然将返回EINVAL错误
pthread_cancel
int pthread_cancel(pthread_t thread); //成功 0 失败 errno
杀死线程,对应进程中的kill函数
该函数不是立即结束线程,而是需要线程达到某个取消点时才能结束
#include <stdio.h>#include <pthread.h>void* thread_func(void* ptr){ // 因为这个线程没有cancel point while(1) { // 关闭cancel检测 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); sleep(10); // 打开cancel检测 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); // 检查cancel point pthread_testcancel(); } return NULL;}int main(){ pthread_t tid; pthread_create(&tid, NULL, thread_func, NULL); // 让线程退出 pthread_cancel(tid); // 等待线程退出 pthread_join(tid, NULL);}
被取消的线程,退出值定义在Linux的pthread库中。常数PTHREAD_CANCELED的值是-1。可在头文件pthread.h中找到它的定义:#define PTHREAD_CANCELED ((void *) -1)。因此当我们对一个已经被取消的线程使用pthread_join回收时,得到的返回值为-1。
pthread_equal
int pthread_equal(pthread_t t1, pthread_t t2);
比较两个线程ID是否相等
线程属性
对应pthread_create函数的const pthread_attr_t *attr参数
typedef struct
{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
} pthread_attr_t;
属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。之后须用pthread_attr_destroy函数来释放资源。
线程属性初始化
int pthread_attr_init(pthread_attr_t *attr); //成功 0 失败 errno //初始化int pthread_attr_destroy(pthread_attr_t *attr); //成功 0 失败 errno //销毁
设置线程属性,分离or非分离
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
- 获取程属性,分离or非分离
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
参数:
- attr:已初始化的线程属性
- detachstate:
PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD _CREATE_JOINABLE(非分离线程)
其他属性使用并不常用,暂时还没学习
线程同步
pthread_mutex_init函数pthread_mutex_destroy函数pthread_mutex_lock函数pthread_mutex_trylock函数pthread_mutex_unlock函数以上5个函数的返回值都是:成功返回0, 失败返回错误号。 pthread_mutex_t 类型,其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。pthread_mutex_t mutex; 变量mutex只有两种取值1、0。
pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
初始化一个锁,参数一传入&mutex,参数二,假mutex为全局或者静态的并且用PTHREAD_MUTEX_INITIALIZER进行初始化可以不用init函数,而局部变量则应采用动态初始化,传入NULL
pthread_mutex_destory
int pthread_mutex_destroy(pthread_mutex_t *mutex); //销毁锁
pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *mutex); //加锁
pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex); //解锁
pthread_mutex_trylock
int pthread_mutex_trylock(pthread_mutex_t *mutex); //尝试加锁 加锁失败直接返回错误号,不阻塞
读写锁
pthread_rwlock_init函数pthread_rwlock_destroy函数pthread_rwlock_rdlock函数 pthread_rwlock_wrlock函数pthread_rwlock_tryrdlock函数pthread_rwlock_trywrlock函数pthread_rwlock_unlock函数以上7 个函数的返回值都是:成功返回0, 失败直接返回错误号。 pthread_rwlock_t类型 用于定义一个读写锁变量。pthread_rwlock_t rwlock;
pthread_rwlock_init
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); //初始化读写锁 attr通常传NULL
pthread_rwlock_destroy
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); //销毁
pthread_rwlock_rdlock
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); //以读方式请求读写锁
pthread_rwlock_wrlock
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); //以写方式请求读写锁
pthread_rwlock_unlock
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); //解锁
pthread_rwlock_tryrdlock
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); //非阻塞读方式请求读写锁
pthread_rwlock_trywrlock
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); //不解释
条件变量
pthread_cond_init函数pthread_cond_destroy函数pthread_cond_wait函数pthread_cond_timedwait函数pthread_cond_signal函数pthread_cond_broadcast函数以上6 个函数的返回值都是:成功返回0, 失败直接返回错误号。pthread_cond_t类型 用于定义条件变量pthread_cond_t cond;
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);int pthread_cond_destroy(pthread_cond_t *cond); //前两个初始化和销毁同上边差不多int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);//阻塞等待条件变量cond满足并释放已掌握的互斥锁//当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);//限时等待一个条件变量//参看man sem_timedwait函数,查看struct timespec结构体。int pthread_cond_signal(pthread_cond_t *cond); //唤醒至少一个阻塞在条件变量上的线程int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒全部阻塞在条件变量上的线程
信号量
头文件 <semaphore.h>sem_init函数sem_destroy函数sem_wait函数sem_trywait函数 sem_timedwait函数 sem_post函数以上6 个函数的返回值都是:成功返回0, 失败返回-1,同时设置errno。(注意,它们没有pthread前缀)sem_t类型,本质仍是结构体。但应用期间可简单看作为整数,忽略实现细节(类似于使用文件描述符)。 sem_t sem; 规定信号量sem不能 < 0。
int sem_init(sem_t *sem, int pshared, unsigned int value); //参1:sem信号量 //参2:pshared取0用于线程间;取非0(一般为1)用于进程间 //参3:value指定信号量初值int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);//参2:abs_timeout采用的是绝对时间。 ↓ 例如定时1秒: time_t cur = time(NULL); 获取当前时间。 struct timespec t; 定义timespec 结构体变量t t.tv_sec = cur+1; 定时1秒 t.tv_nsec = t.tv_sec +100; sem_timedwait(&sem, &t); 传参
互斥量
pthread_mutexattr_t mattr 类型: 用于定义mutex锁的【属性】
int pthread_mutexattr_init(pthread_mutexattr_t *attr); //初始化
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); //销毁
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); //修改
参2:pshared取值: 线程锁:PTHREAD_PROCESS_PRIVATE (mutex的默认属性即为线程锁,进程间私有) 进程锁:PTHREAD_PROCESS_SHARED
文件锁
借助 fcntl函数来实现锁机制。 操作文件的进程没有获得锁时,可以打开,但无法执行read、write操作。
int fcntl(int fd, int cmd, ... /* arg */ );
参2:
F_SETLK (struct flock *) 设置文件锁(trylock)
F_SETLKW (struct flock *) 设置文件锁(lock)W –> wait
F_GETLK (struct flock *) 获取文件锁参3:
struct flock {
…
short l_type; 锁的类型:F_RDLCK 、F_WRLCK 、F_UNLCK
short l_whence; 偏移位置:SEEK_SET、SEEK_CUR、SEEK_END
off_t l_start; 起始偏移:1000
off_t l_len; 长度:0表示整个文件加锁
pid_t l_pid; 持有该锁的进程ID:(F_GETLK only)
…
};
- Linux 线程简析
- linux中断线程化分析
- linux 线程 线程属性
- linux 线程 线程同步
- linux 线程
- linux线程
- Linux 线程
- linux 线程
- linux线程
- Linux “线程”
- Linux线程
- linux 线程
- linux 线程
- linux线程
- linux 线程
- linux--线程
- Linux线程
- linux 线程
- 函数式编程常用术语
- 最短路+差分约束学习笔记
- css布局总结
- JZOJ 4061. 【JSOI2015】字符串树
- Android 的简单应用(MusicPlayer) 以及 Soundpool池
- Linux 线程简析
- leecode 解题总结:70. Climbing Stairs
- THUWC2017 酱油记
- boost处理日期和时间的总结
- 【JSOI2015】送礼物
- Servlet3.0包依赖问题
- 从栈和堆中来看值传递和引用传递
- Linux 常用环境变量及作用和环境变量文件的详细介绍及其加载执行顺序
- PAT甲级真题1012. The Best Rank (25)