三.线程
线程的概念
一个正在运行的函数
posix线程是一套标准,而不是实现
openmp线程
线程标识:pthread_t (posix线程下的线程标识)
// pthread_equal; -> 比较线程标识是否相同
int pthread_equal(pthread_t t1,pthread_t t2);
// pthread_t pthread_self -> 获取当前线程的线程标识
pthread_t pthread_self(void);
线程的创建
// pthread_create(); -> 创建一个线程
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg);
// 参数:pthread_t * thread:回填的线程标识
// const pthread_attr_t *restrict attr:规定的线程属性
// void *(*start_routine)(void *):函数指针,兄弟线程
// void * arg:兄弟线程的参数
// 返回值:成功:0,失败:error number
//restrict关键字指定了在函数生命周期内,attr指向的内存不会通过任何其他指针被访问。
线程的调度取决于调度器策略
线程的终止
终止方式:线程从启动例程返回,返回值为线程的推出码
线程可以被同一进程中的其他线程取消
线程调用pthread_exit()
函数
// pthread_exit(); -> 正常终止线程
void pthread_exit(void *retval);
// pthread_join(); -> 类似wait();
int pthread_join(pthread_t thread,void **retval);
线程栈的清理
// 本质是宏 成对出现
// 类似与钩子函数,但是钩子函数对挂钩子有主动权,但是对取钩子函数中的内容没有主动权
// pthread_cleanup_push(); -> 挂钩子
void pthread_cleanup_push(void (*routine)(void *), void *arg);
// 参数:函数指针,函数参数
// pthread_cleanup_pop(); -> 取钩子函数中的内容
void pthread_cleanup_pop(int execute);
// 参数:是否调用 0/1
线程的取消选项
// pthread_cancel(); -> 取消一个线程
int pthread_cancel(pthread_t thread);
// 取消状态:允许和不允许
// 允许取消:异步cancel和推迟cancel
// 推出取消(默认):推迟至cancel点在响应
// cancel点:POSIX定义的cancel点,都是可能引发阻塞系统调用
// pthread_setcancelstate(); -> 设置是否允许取消
int pthread_setcancelstate(int state, int *oldstate);
// pthread_setcanceltype(); -> 设置取消方式
int pthread_setcanceltype(int type, int *oldtype);
// pthread_testcancel(); -> 本函数啥也不做,单纯的取消点
void pthreadn_testcancel(void);
// "杀"掉一个线程的步骤:先pthread_cancel();后pthread_join();
线程分离
// pthread_detach(); -> 分离一个线程,不能被join回来
int pthread_detach(pthread_t thread);
线程竞争
线程竞争是指多个线程在同时访问共享资源时,由于执行顺序不确定或者执行时间片长度的随机性,导致对该资源的操作产生了冲突,从而引发程序错误或者结果不正确的情况。
常见的线程竞争问题包括死锁、饥饿和竞态条件等。其中,死锁是指两个或多个进程互相等待对方释放所持有的资源,导致所有进程都无法继续执行;饥饿则是指某些进程长期得不到执行机会,因为其他进程一直占用着资源;竞态条件则是指多个线程试图同时修改一个共享数据结构,导致最终结果依赖于执行顺序,从而产生不确定性。
为了避免线程竞争,可以采用各种同步机制,如锁、信号量、条件变量等,保证每次只有一个线程能够访问共享资源,从而避免产生冲突。此外,在设计并发程序时也需要考虑业务逻辑以及各种异常情况,以充分保证程序的正确性和可靠性。
线程同步
互斥量:bool
// pthread_mutex_t 类型 定义互斥量
// pthread_mutex_init(); -> 动态初始化,可在参数处修改线程属性
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
// pthread_mutex_destroy(); -> 销毁互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);
// 静态初始化,默认线程属性
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// pthread_mutex_lock)(); -> 死心眼获得锁,阻塞系统调用
int pthread_mutex_lock(pthread_mutex_t *mutex);
// pthread_mutex_trylock(); -> 尝试获得锁,不阻塞系统调用
int pthread_mutex_trylock(pthread_mutex_t *mutex);
// pthread_mutex_unlock(); -> 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 锁区间被称为临界区
互斥量(mutex)和锁(lock)都是用于同步并发程序的机制,它们可以防止多个线程同时访问共享资源。简单来说,互斥量和锁都是用于实现线程之间的互斥。
互斥量是一种特殊的变量,用于控制对共享资源的访问。当一个线程想要访问共享资源时,它会尝试获取互斥量。如果互斥量当前没有被其他线程锁定,则该线程会锁定互斥量,并且可以访问共享资源。如果互斥量已经被其他线程锁定,则该线程将被阻塞,直到互斥量被释放为止。
锁的概念和互斥量类似,但是锁通常是一个更加通用的概念。锁可以是互斥量,也可以是其他类型的同步原语,例如信号量。在某些情况下,使用锁比使用互斥量更加灵活。
总的来说,互斥量和锁都是用于同步并发程序的机制,它们可以防止多个线程同时访问共享资源,从而避免竞态条件等问题的发生。
条件变量:
// pthread_cond_t 类型,定义条件变量
// pthread_cond_init(); -> 动态初始化
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
// 静态初始化
// pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// pthread_cond_destory(); -> 销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
// pthread_cond_broadcast(); -> 唤醒所有等待
int pthread_cond_broadcast(pthread_cond_t *cond);
// pthread_cond_signal(); -> 唤醒任意一个等待
int pthread_cond_signal(pthread_cond_t *cond);
// pthread_cond_timewait(); -> 尝试等
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
// pthread-cond_wait(); -> 死等
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
信号量:int
线程相关的属性
线程属性(pthread_attr_t)
// pthread_attr_init();
int pthread_attr_init(pthread_attr_t *attr);
// pthread_attr_destory();
int pthread_attr_destory(pthread_attr_t *attr);
// 具体属性设置见 man 手册
线程同步的属性
互斥量属性
// pthread_mutexattr_t
// pthread_mutexattr_init();
// pthread_mutexattr_destory();
// pthread_mutexattr_setpshared(); -> 用于设置共享属性值。
// pthread_mutexattr_getpshared(); -> 用于获取一个已经初始化的互斥锁属性对象的共享属性值
// pthread_mutexattr_gettype();
// pthread_mutexattr_settype();
条件变量属性
// pthread_condattr_t;
// 与互斥锁属性类似
重入
多线程中的IO 会有unlock版本
线程和信号
// pthread_sigmask();
// sigwait();
// pthread_kill();
线程和fork
openmp标准和线程模式
OpenMP 是一种支持多平台共享内存并行编程的 API,它提供了一系列的指令和库函数,使得开发者可以在 C、C++ 和 Fortran 等语言中方便地实现多线程并行程序。OpenMP 的初衷是为了将并行计算能力带给广大的应用程序开发者,以便他们可以更加容易地利用多核 CPU 的性能优势