线程同步接口。线程问题就是同步问题,解决了这个你对于
线程就没有恐惧了。
线程同步
因为线程共享进程的存储空间,所以进程的栈 全局变量都可以被
线程所访问,但是又因为竞度问题,我们无法判定是哪个线程or进程
先访问这些资源,这就可以出现问题,特别是对于临界区资源,只能
要求同时只有一个进程or线程访问的。所以我们只要解决了对于临界区的
访问就是解决了线程是否安全的问题。我是这样认为的。为了解决这个
同步问题,主要还是互斥量的使用。
互斥量
互斥量是这样一把锁,在访问共享资源之前,对互斥量进行设置(加锁)
访问结束之后,再对互斥量进行设置(解锁)。对互斥量加锁之后,任何其他
试图再去对互斥量加锁的线程就会被阻塞,知道当前线程释放互斥量。我觉得
这跟设置一个 int block 变量一样,初始的时候blokc = 1;加锁则-1 block=0
解锁则+1,;每次加锁之前判断block是否为1,为1 则加锁,否则阻塞。当检测
到block = 1 时,给所有唤醒等待的线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); int pthread_mutex_destory(pthread_mutex_t *mutex); 俩个函数的返回值:若成功,返回0;否则,返回错误编号 attr是互斥量的属性, NULL表示使用的默认属性,具体可以查看文档 pthread_mutex_destory 函数先销毁 */ int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); 返回值:若成功,返回0;否则,返回错误编号 int pthread_mutex_timelock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);
|
读写锁
我觉得大家肯定还记得经典的读写者问题,当然了还有相应的生产者和消费者问题,这个
读写锁就是读写者问题。当已经加上写锁的时候,再加读锁和写锁都会被阻塞。当已经加上读锁
的时候,还可以继续加读锁,但是加写锁会被阻塞。这就是为了保证内容的一致性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 初始化和销毁一个读写锁,通上面的互斥量锁一样 #include <pthread.h> int pthread_rwlock_init(pthread_rwlock *restrict rwlock, const pthread_rwlockattr_t *restrict sttr); int pthread_rwlock_destoru(pthred_rwlock *rwlock); 俩个函数:若成功,返回0;否则返回失败码 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); 返回值:若成功,返回0;否则,返回错误编号 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 返回值:若成功,返回0;否则,返回错误编号 int pthread_rwlock_timerdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr); int pthread_rwlock_timewrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr); 返回值:若成功,返回0;否则,返回错误编号
|
条件锁
当你需要达到一定的条件,才继续运行时,这个就是你需要的锁了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <pthread.h> int pthread_cond_init(pthread_cond *restrict cond, const pthread_condattr_t *restrict sttr); int pthread_cond_destoru(pthred_cond *cond); 俩个函数:若成功,返回0;否则返回失败码 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 返回值:若成功,返回0;否则,返回错误编号 int pthread_cond_timewait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); const struct timespec *restrict tsptr); 返回值:若成功,返回0;否则,返回错误编号 条件锁的mutex参数,是已经加过锁的互斥量,这样做关闭了条件检查 和线程进入休眠状态等待条件改变这俩个操作之间的时间通道。 保证线程不会错过条件的任何变化。因为条件的状态的改变也需要用到 同一个互斥量锁,把该线程放到条件等待队列之后,就解锁互斥量 函数返回时 重新加锁互斥量 */ int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); 俩个函数返回值:若成功,返回0;否则,返回错误编号 互斥量加锁锁之后 改变条件状态 解锁 之后才能 发送信号 通知等待线程
|
自旋锁
互斥量锁,在没有资源的时候,是直接阻塞,自旋锁不通过休眠使线程阻塞
而是在获取锁之前一直处于盲等(自旋转),自选锁主要用于一下情况:锁被持有
的时间段,而且线程不希望在重新调度上花费太多成本。因为自旋锁盲等,我认为
是直接while(true) ,所以会在等待期间会一直占用cpu资源,所以适合短锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 初始化和销毁一个自旋锁,通上面的互斥量锁一样 #include <pthread.h> int pthread_spin_init(pthread_spin *restrict spin, const pthread_spinattr_t *restrict sttr); int pthread_spin_destory(pthred_spin *spin); 俩个函数:若成功,返回0;否则返回失败码 int pthread_spin_lock(pthread_spin_t *spin); int pthread_spin_trylock(pthread_spin_t *spin); int pthread_spin_unlock(pthread_spin_t *spin); 返回值:若成功,返回0;否则,返回错误编号 int pthread_spin_tryrdlock(pthread_spin_t *spin); int pthread_spin_trywrlock(pthread_spin_t *spin); 返回值:若成功,返回0;否则,返回错误编号
|
屏障
等待到一定数量的线程都调用 pthread_barrier_wait
之后唤醒所有因为调用pthread_barrier_wait 休眠的线程。
1 2 3 4 5 6 7 8 9 10 11 12
| 初始化和销毁一个屏障 #include <pthread.h> int pthread_barrier_init(pthread_barrier *restrict barrier, const pthread_barrierattr_t *restrict sttr, unsigned int count); int pthread_barrier_destory(pthred_barrier *barrier); 俩个函数:若成功,返回0;否则返回失败码 int pthread_barrier_wait(pthread_barrier_t *barrier); 返回值:若成功,返回0;否则,返回错误编号 调用pthread_barrier_wait 的线程在屏障技术为满足条件时,会进入 休眠。如果该线程是最后一个调用pthread_barrier_wait的线程,满 足了屏障技术,所有的线程被唤醒。
|