目录
1.线程同步相关概念
2.锁属性-建议锁
3.Mutex互斥锁操作
4.互斥锁使用注意事项
5.互斥量的初始化方法
6.死锁
7.读写锁特性
8.读写锁操作函数
9.读写锁使用示例
10.条件变量操作函数
11.生产者消费者模型简单分析
12.条件变量实现生产者消费者模型代码预览
13.条件变量实现生产者消费者模型流程分析
14.条件变量实现生产者消费模型
15.wait放置到while循环中
16.信号量和信号量操作函数
17.信号量生产者消费者图示分析
18.信号量生产者消费者代码示例
1.线程同步相关概念
同步:即协同步调,按预定的先后次序访问共享数据。
- 线程同步,指一个线程发出某一功能调用时,没有得到结果之前,该调用不返回。同时,其他线程为保证数据一致性,不调用该功能。


2.锁属性-建议锁
数据混乱产生的原因
1. 资源共享
2. 调度随机
3. 线程之间缺乏必要同步机制。
- 只能从第3步着手解决 “数据混乱”, 避免产生与时间有关的错误。执行多个控制流(线程)访问共享的数据的先后顺序。
- 锁的属性:
- 系统提供内用户使用的所有的锁,全部为 “建议锁”, 不具备强制性。
- 如果访问共享数据的线程,不拿锁,直接访问共享数据,能访问成功。但是,数据就有出现混乱的风险。 —— 拿锁与否完全取决于编写的程序。


3.Mutex互斥锁操作
```c
pthread_mutex_t mutex; 创建互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); 初始化互斥锁
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_destroy(pthread_mutex_t *mutex); 销毁互斥锁
```

4.互斥锁使用注意事项
- 注意事项:
1. 保证锁的粒度 , 越小越好!( 访问共享数据前,加锁,访问共享数据结束,立即解锁。)
2. 互斥锁,本质:结构体。 我们可以把它当成整数看待。 初值为 1(init成功)。 取值:1 或 0
3. 加锁:--操作。1-- ---> 0 。 失败:阻塞线程。
4. 解锁:++操作。0++ ---> 1。 成功:唤醒阻塞在锁上的线程。
5. try锁:尝试加锁。 成功--。 失败,设置错误号 EBUSY、EINVAL

5.互斥量的初始化方法
- 初始化互斥量
```c
pthread_mutex_t mutex;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 静态初始化。--- 常用于全局变量
pthread_mutex_init(&mutex, NULL); // 动态初始化。--- 常用于函数内部初始化。
```

6.死锁
- 死锁不是一种锁!是错误使用锁的一种状态。
- 常见死锁种类:
1. 对一把锁,反复lock。
2. 两个线程, 各自持有一把锁,请求另一把锁。

7.读写锁特性
### 3句话描述
1. 锁,只有一把!可以指定为 “读模式” 和 “写模式”
2. 读共享,写独占。
3. 写锁优先级高!( 读锁已经加锁成功,写锁无法加锁成功,阻塞等。)


8.读写锁操作函数
```c
pthread_rwlock_t rwlock; // 创建读写锁对象。
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 静态初始化读写锁。
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr); // 动态初始化读写锁。
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 读模式加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 写模式加锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 解锁
```


9.读写锁使用示例
restrict 关键字:
- 用来修饰指针变量。被该关键字修饰的指针变量所指向的内存操作,只能由本指针完成。

10.条件变量操作函数
条件变量不是锁! 结合 互斥锁 使用。 也能造成线程阻塞。




11.生产者消费者模型简单分析


12.条件变量实现生产者消费者模型代码预览


13.条件变量实现生产者消费者模型流程分析
```c
void err_thread(int ret, char *str)
{
if (ret != 0) {
fprintf(stderr, "%s:%s\n", str, strerror(ret));
pthread_exit(NULL);
}
}
// 创建公共区
struct msg {
int num;
struct msg *next;
};
struct msg *head = NULL;

14.条件变量实现生产者消费模型
void *producer(void *arg)
{
while (1) {
struct msg *p = malloc(sizeof(struct msg));
// 生产数据
p->num = rand() % 1000 + 1;
printf("----produce:%d\n", p->num);
// 将数据保存至公共区
pthread_mutex_lock(&mutex);
p->next = head;
head = p; // 头插法,写入数据
pthread_mutex_unlock(&mutex);
// 通知消费者
pthread_cond_signal(&has_data);
sleep(rand() % 3);
}
return NULL;
}


15.wait放置到while循环中
int main(int argc, char *argv[])
{
int ret;
pthread_t pid, cid;
srand(time(NULL)); // 播种随机数种子
// 生产者
ret = pthread_create(&pid, NULL, producer, NULL);
if (ret != 0)
err_thread(ret, "pthread_create producer:");
// 消费者
ret = pthread_create(&cid, NULL, consumer, NULL);
if (ret != 0)
err_thread(ret, "pthread_create consumer:");
ret = pthread_create(&cid, NULL, consumer, NULL);
if (ret != 0)
err_thread(ret, "pthread_create consumer:");
ret = pthread_create(&cid, NULL, consumer, NULL);
if (ret != 0)
err_thread(ret, "pthread_create consumer:");
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}


16.信号量和信号量操作函数
- 信号量与信号无关!
- 信号量 相当于 初始值为 N 的互斥量。 N值,表示可以同时访问共享数据的线程数。


17.信号量生产者消费者图示分析
```c
#include <semaphore.h>
sem_t sem; // 创建信号量对象
int sem_init(sem_t *sem, int pshared, unsigned int value); //动态初始化方法。没有静态
参2 pshared: 0: 用于线程间同步
非0(1): 用于进程间同步
参3 value:信号量的初值。 N值。代替同时访问共享数据的线程个数。 返回值:
成功:0
失败:-1, errrno --- perror()
int sem_destroy(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* 纳秒 Nanoseconds [0 .. 999999999] */
};
abs:绝对。
绝对时间:从 1970年1月1日 00:00:00 开始,计算时间。
int sem_wait(sem_t *sem);
一次调用,做一次--操作。当信号量的值为0时, 再次--,会阻塞。(对比pthread_mutex_lock)
18.信号量生产者消费者代码示例


