线程同步接口。线程问题就是同步问题,解决了这个你对于
线程就没有恐惧了。


线程同步

因为线程共享进程的存储空间,所以进程的栈 全局变量都可以被
线程所访问,但是又因为竞度问题,我们无法判定是哪个线程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表示使用的默认属性,具体可以查看文档
/* 调用malloc分配内存的锁 需要使用pthread_mutex_init函数初始化,释放内存前需要使用
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的线程,满
足了屏障技术,所有的线程被唤醒。