线程
线程不同于进程,线程可以说是一种轻量级的进程。
只给每个线程分配自己的栈空间,其他的资源都是和父线程共享的。
子线程和父线程构成整个进程。
每个进程至少有一个线程。
线程创建
1 | #include <pthread.h> |
线程的入口是一个函数,当该函数执行完,线程也就结束。
start_rtn 就是线程的入口函数指针。
该函数参数为void 返回值位void 所以当要传入多个参数的时候要将其
封装成结构体,然后传入,同理,返回值也是这样。
线程的终止
线程的终止有很多中方式
1 线程从它所执行的函数中返回,返回值是线程的退出码
2 线程可以被同一进程中的其他线程取消
3 进程调用里pthread_exit1
2
int pthread_exit(void *rval_ptr);
当线程执行pthread_exit退出后,其他线程可以通过执行pthread_join来访问到rval_ptr这个指针1
2
int pthread_join(pthread_t thread, void **rval_ptr);
其他线程在调用pthread_join函数之后会将一直阻塞,直到指定的线程退出。
类似进程中的wait
当调用pthread_join时会让指定的线程进入分离状态。
所谓分离状态就是线程的底层资源可以在线程终止时立即被回收。
可以通过调用pthread_detach来设置分离状态。1
2
int pthread_detach(pthread_t tid);
pthread_cancel 用来取消同一进程中的其他进程。1
pthread_cancel(pthread_t tid)
被指定的线程并不一定会被取消,得看线程的属性
用pthread_setcancelstate()
用来设置cancel属性
PTHREAD_CANCEL_DISABLE 线程不会被cancel
PTHREAD_CANCEL_ENABLE 线程可以被cancel
同时需要设置cancel的类型。
使用pthread_setcanceltype()
用来设置cancel的类型
PTHREAD_CANCEL_FEFERRED 线程遇到断点函数才会被cancel
PTHREAD_CANCEL_ASYNCHRONOUS 线程会被无条件cancel
线程的属性设置
在创建线程的函数中可以通过传递传递pthread_attr_t的结构体指针来设置线程的属性
所以通过函数来设置这个结构体的各个值
通过查找可以发现pthread_attr_t是一个联合体
1 | union pthread_attr_t |
1 线程属性的初始化和销毁1
2
3
4
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
2 线程属性的设置。
一般来说每种属性对应一个get函数和一个set函数pthread_attr_set***
pthread_attr_get***
1
2
3
4
5
6
7
8pthread_attr_getstacksize
pthread_attr_setstacksize //set/get stack size attribute in thread attributes object
pthread_attr_getdetachstate
pthread_attr_setdetachstate //set/get detach state attribute in thread attributes object
pthread_attr_getguardsize
pthread_attr_setguardsize //get and set the thread guardsize attribute
............
............
线程的同步
同一进程中各个线程都共享资源,那么当多个线程都使用同一资源就会有问题
这时就需要线程总的各种同步手段里
- 线程锁(互斥量)
和线程锁相关的函数都是像这样的
pthreadmutex*
线程锁的初始化1
2int pthread_mutex_init(pthread_mutex *restrict mutex,
const phtread_mutexattr_t *restrict mutex);
或者直接1
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER ;
线程锁的销毁1
int pthread_destroy(pthread_mutex *mutex);
从线程锁的初始化可以看出,给初始化函数传属性参数
所以得先初始化线程锁属性
线程锁属性的初始化1
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
线程锁属性的销毁1
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
线程锁属性的设置1
2
3
4int pthread_mutexattr_settype(pthread_mutexattr_t *attr,
int type);
int pthread_mutexattr_gettype(pthread_mutexattr_t *attr
int *restrict type);
type的值根据宏值来传入,大概有以下这几个宏值
PTHREAD_MUTEX_NORMAL
This type of mutex does not detect deadlock. A thread attempting to relock this mutex without first unlocking it shall deadlock.
Attempting to unlock a mutex locked by a different thread results in undefined behavior. Attempting to unlock an unlocked mutex
results in undefined behavior.
PTHREAD_MUTEX_ERRORCHECK
This type of mutex provides error checking. A thread attempting to relock this mutex without first unlocking it shall return with an
error. A thread attempting to unlock a mutex which another thread has locked shall return with an error. A thread attempting to
unlock an unlocked mutex shall return with an error.
PTHREAD_MUTEX_RECURSIVE
A thread attempting to relock this mutex without first unlocking it shall succeed in locking the mutex. The relocking deadlock which
can occur with mutexes of type PTHREAD_MUTEX_NORMAL cannot occur with this type of mutex. Multiple locks of this mutex shall require
the same number of unlocks to release the mutex before another thread can acquire the mutex. A thread attempting to unlock a mutex
which another thread has locked shall return with an error. A thread attempting to unlock an unlocked mutex shall return with an
error.
PTHREAD_MUTEX_DEFAULT
Attempting to recursively lock a mutex of this type results in undefined behavior. Attempting to unlock a mutex of this type which
was not locked by the calling thread results in undefined behavior. Attempting to unlock a mutex of this type which is not locked
results in undefined behavior. An implementation may map this mutex to one of the other mutex types.
线程锁加解锁1
2
3int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
pthread_mutex_lock
对一个线程锁加锁,根据线程锁的属性,会有不同的结果。
pthread_mutex_unlock
解锁一个进程,根据线程锁的属性,会有不同的结果
pthread_mutex_trylock
它的功能和pthread_mutex_lock差不多,但是如果不能加锁,该函数也会立刻返回
- 读写锁(rwlock)
和读写锁相关的函数大多数是这样的
pthread_rwlock**
读写锁一般那用于读多写少的环境
读的时候只要没有写线程就行
写的时候需要其他线程都停止操作
所以说类似一写多读的感觉;
读写锁的初始化和销毁1
2
3pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr)
pthread_rwlock_destroy(pthread_rwlock_t *restrict rwlock);
读写锁的属性设置1
2
3
4
5
6pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
pthread_rwlockattr_setpshared(pthread_rwlockattr_t * restrict attr,
int pshared)
pthread_rwlockattr_getpshared(pthread_rwlockattr_t *restrict attr,
int *restrict pshared)
读写锁的加解锁1
2
3
4
5pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
- 自旋锁(spinlock)
普通的线程锁:一方线程等待线程锁被解锁的时候,线程的cpu会被剥夺,进入休眠,
当解锁之后线程再被唤醒,这个休眠和唤醒的过程会消耗时间
自旋锁:在等待的过程中,cpu资源并不会被剥夺,时刻等待着锁被其他线程解锁
通过测试程序来看,采用自旋锁的线程在等待过程中极其消耗cpu
所以自旋锁快,但是消耗资源。普通线程锁慢,节省资源
自旋锁用于临界区极短的情况
自旋锁的初始化和销毁1
2pthread_spin_init(pthread_spinlock_t *lock, int pshared);
pthread_spin_destroy(pthread_spinlock_t *lock);
自旋锁的加解锁1
2
3pthread_spin_lock(pthread_spinlock_t *lock);
pthread_spin_unlock(pthread_spinlock_t *lock);
pthread_spin_trylock(pthread_spinlock_t *lock);
- 条件变量
条件变量是利用线程间共享全局变量进行同步的一种机制,主要包括两个
动作:一个线程等待条件变量的条件成立而挂起
另一个线程使条件成立(给出条件成立信号)
条件变量常常是和线程锁一块使用
如果通过加解锁的方法来实现,是条件成立的线程并不能成为老大,
可能它还没来的及加解锁,锁就被其他线程抢去了。
所以必须能保证那个能使条件成立的线程只负责给条件,其他线程只负责接受条件就好
条件变量的初始化和销毁1
2
3pthread_cond_init(pthread_cond_t *retrict cond
const pthread_condattr_t *restrict attr);
pthread_cond_destroy(pthread_cond_t *restrict cond);
或者直接使用宏来赋值
pthread_cond_t cond = PTHREAD_COND_INITIALIZER
等待条件成立1
2
3
4
5pthread_cond_wait(pthread_cond_t *restrict cond
pthread_mutex_t *restrict mutex);
pthread_cond_timewait(pthread_cond_t *restrict cond
pthread_mutex_t *restrict mutex
const struct timespec *restrict mutex)
让条件成立1
2pthread_cond_signal(pthead_cond_t *restrict cond);
pthread_cond_broadcast(pthread_cond_t *restrict cond);
signal只会激活一个等待的线程
而broadcast会激活所有正在等待的线程。
条件变量的使用方式
1)pthread_mutex_lock(mutex);
2)pthread_cond_wait(cond, mutex);
3)pthread_mutex_unlock(mutex);
从表面上看,第3步解的是第1步加的锁,其实不是
pthread_cond_wait内部在执行的过程中
先解锁—-wait—-加锁
所以第3步解的锁其实是第2步加的锁。
这样使用就保证里线程的被按顺序激活。
用于线程之间流程化的设计。
会造成死锁的几种情况:
锁中锁,一个线程给自己加锁之后再一次加锁
多线程穿插加解多把锁。
锁的异常(锁未初始化,锁的数据结构被破坏)
内核故障:虽然发生的几率基本位零,但是还是有可能
锁不符合规范(在使用锁的时候,尽量减少子程序中的结束语句,出口越少越好)