一、线程标识
每个线程有一个线程ID,线程ID用pthread_t类型表示,可移植操作系统一般不能把它作为整数处理。
Linux下的pthread,一般的实现是:
进程ID: pid_t pid; //pid_t: unsigned int
线程ID:pthread_t tid; //pthread_t: unsigned long int
线程ID比较:
#include<pthread.h>
int pthread_equal(pthead_t t1, pthread_t t2);
获取自身线程ID:
#include<pthread.h>
pthread_t pthread_self(void);
二、线程创建
#inlude<pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t attr, void(start_routine)(void), void *arg);
thread:出参, 返回线程号;
attr:线程属性,一般是NULL;
start_routine: 线程的运行函数实体;
arg: 线程的运行函数入参;
三、线程终止
进程中任一线程调用了exit,_Exit或_exit则整个进程都会终止,类似的,如果信号的默认动作是终止进程,那么,把该信号发送到线程也会终止整个进程。
单个线程退出有三种方式:
(1)、线程只是从启动例程中返回,返回值是线程的退出码;
(2)、线程可以被同一进程中其他线程取消;
(3)、线程调用pthread_exit;
#include<pthread.h>
void pthread_exit(void *retval);
int pthread_join(pthread_t thread, void **retval);
pthread_join用来等待线程的终止,调用线程将一直阻塞,直到线程调用pthread_exit、从启动例程返回或被取消。
四、线程创建
#include <pthread.h>
int pthread_cancel(pthread_t thread);
pthread_cancel函数用来取消同一进程中其他线程。默认情况下制定的tid线程相当于调用了参数为PTHREAD_CANCELED的pthread_exit函数;该函数仅发送取消请求,线程可以选择忽略取消方法或者控制取消方式;
#include<pthread.h>
int pthread_detach(pthread_t thread);
pthread_detach函数可以使线程进入分离状态,分离状态时,线程的底层存储资源可以在线程终止时立即被收回,且不能用pthread_join函数等待它的终止。
五、线程同步
竞争条件与临界区:
多个线程同时访问同一个资源且结果与线程访问资源时的顺序有关的这样一种情形就叫竞争条件。
临界区指一个访问共同资源的程序片段。
互斥量:
通过使用pthread的互斥接口保护数据,确保同一时间只有一个线程访问数据。互斥量本质上是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞直至当前线程释放该互斥锁。
互斥量初始化:
互斥量使用前需要先初始化,可以初始化为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量);也可以通过调用pthread_mutex_init函数初始化,如果动态分配互斥量,释放内存前需要调用:
pthread_mutex_destory
#include<pthread.h>
int pthread_mutex_destory(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
加锁与释放锁:
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *mutex);
其中,如果互斥量已经上锁,则调用pthread_mutex_lock会阻塞直到互斥量解锁,如果不希望阻塞,则使用pthread_mutex_trylock,当互斥量未上锁则加锁,已加锁则出错返回EBUSY。
死锁:
是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程或线程称为死锁进程或线程。
产生死锁的四个必要条件:
A\ 互斥条件:一个资源每次只能被一个进程或线程使用。
B\ 请求与保持条件:一个进程或线程因请求资源而阻塞时,对已获得的资源保持不放。
C\ 不剥夺条件:此进程或线程已获得的资源,在未使用完之前,不能强行剥夺。
D\ 循环等待条件:多个进程或线程之间形成一种头尾相接的循环等待资源关系。
读写锁:
读写锁与互斥量类似,不过读写锁允许更高的并行性。互斥量要么是加锁状态,要么是不加锁状态。而读写锁有三种状态:读模式加锁状态、写模式加锁状态、不加锁状态。一次只有一个线程可以占有写模式的读写锁,但多个线程可以同时占有读模式的读写锁。
读写锁是写加锁状态时,在这个锁解锁之前,所有试图对这个锁加锁的线程都会被阻塞。当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是如果线程希望以写模式对此锁加锁,它必须阻塞直到所有的线程释放读锁。
一句话:当有线程要写的时候,谁都不能读;当有线程读的时候,大家都可以读,但是谁都不能写;适用于读的次数远大于写的次数的情况;
#include<pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
条件变量:
条件变量与互斥量一起使用时,允许线程以五竞争的方式等待特定的条件发生。而条件变量本身需要互斥量保护,线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种变化,因此必须锁住互斥量以后才能计算条件。
#include<pthread.h>
int pthread_cond_int(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_timedwait(pthreadc_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_signal
int pthread_cond_broadcast