No.1 线程同步
一、互斥量:互相之间排斥
mutex
1. 初始化 pthread_mutex_init
2. 上锁 pthread_mutex_lock //加锁,如果已经枷锁则会阻塞
3. 判断是否上锁 pthread_mutex_trylock //lock的非阻塞版,未加锁返回0
4. 解锁 pthread_mutex_unlock //解锁
5. 销毁锁 pthread_mutex_destroy
int pthread_mutex_init(
pthread_mutex_t *restrict mutex, //初始化
const pthread_mutexattr *restrict attr); //标志,状态
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//可以直接赋值初始化
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 *mute);
整体很合理,但是过程很不合理,f1或者f2长时间执行后才会轮到另外的一个线程。并且是阻塞的。
二、条件变量:一般互斥量和条件变量一起使用。
条件变量:
cond
1. 初始化 pthread_cond_init
2. 条件变量等待 pthread_cond_wait
3. 发信号给条件变量 解除等待 pthread_cond_wait
int pthread_cond_init(
pthread_cond_t *restrict cond, //初始化
const pthread_condattr *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
cond也有临界数据脏的问题,因此要上锁。
cond与mutex结合使用:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> //sleep
pthread_cond_t cond; //定义条件变量
pthread_mutex_t mutex;
int n = 0;
int m = 50000000;
void* f1()
{
for(int i=0;i<m;i++)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);//阻塞式等待
n++;
//printf("线程一==========%d\n",n);
printf("%d\n",n);
pthread_mutex_unlock(&mutex);
}
}
void* f2()
{
for(int i=0;i<m;i++)
{
pthread_cond_wait(&cond,&mutex);//阻塞式等待
printf("线程二++++++%d\n",n);
}
}
int main()
{
pthread_t p1,p2;
//初始化互斥量
pthread_cond_init(&cond,NULL);
pthread_mutex_init(&mutex,NULL);
pthread_create(&p1,NULL,f1,NULL);
pthread_create(&p2,NULL,f1,NULL);
for(int i=0;i<m*2;i++)
{
pthread_cond_signal(&cond);
}
pthread_join(p1,NULL);
pthread_join(p2,NULL);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
printf("n:%d,m:%d\n",n,m*2);
return 0;
}
三、读写锁
读锁 写锁
读读 相容:一个线程在读的时候,其他线程也可以读
写写、读写 相斥:某个线程在写的时候,其他线程不能操作
pthread_rwlock_t
1. 初始书 pthread_rwlock_init
2. 获取 pthread_rwlock_wrlock pthread_rwlock_rdlock
3. 释放 pthread_rwlock_unlock
4. 销毁 pthread_rwlock_destroy
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> //sleep
pthread_rwlock_t rwlock; //定义读写锁
int n = 0;
int m = 50000000;
void* read_()
{
for(int i=0;i<m;i++)
{
pthread_rwlock_rdlock(&rwlock);
printf("读操作==============\n");
pthread_rwlock_unlock(&rwlock);
}
}
void* write_()
{
for(int i=0;i<m;i++)
{
pthread_rwlock_wrlock(&rwlock);
printf("写操作-----------\n");
pthread_rwlock_unlock(&rwlock);
}
}
void* read2()
{
for(int i=0;i<m;i++)
{
pthread_rwlock_rdlock(&rwlock);
printf("读操作++++++++++\n");
pthread_rwlock_unlock(&rwlock);
}
}
int main()
{
pthread_t p1,p2;
//初始化读写锁
pthread_rwlock_init(&rwlock,NULL);
pthread_create(&p1,NULL,read_,NULL);
pthread_create(&p2,NULL,read2,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
printf("n:%d,m:%d\n",n,m*2);
printf("rwlock : %d\n",rwlock);
int ret = pthread_rwlock_destroy(&rwlock);
printf("des:%d\n",ret);
return 0;
}
它跟互斥锁有同样的问题,但是他影响不打,没有关系
读写锁和互斥锁是一样的:
都是上锁后排斥其他锁,本身有锁的前提下可以通过trylock来提前返回
但是本质上面还是要等 还是阻塞的。
四、自旋锁
一直循环检查锁是否可用,如果不可用那就继续循环,如果可用,那就用。
pthread_spinlock_t
1. 初始化 pthread_spin_init
2. 获取 pthread_spin_lock
3. 解锁 pthread_spin_unlock
4. 销毁 pthread_spin_destroy
五、选择方法
- 自旋锁建立锁的小号比较小,但是当线程阻塞的时候,一直循环的消耗比较大。所以当阻塞情况比较多的时候,不建议使用。
- 互斥锁建立的消耗比较大,但当线程阻塞的时候,它阻塞所以无消耗,所以当阻塞时间比较多,索锁的数量比较少的时候,使用互斥锁比较划得来。
首先看锁的数量,其次看阻塞是否比较多。
例如:
10个线程 加锁线程比较少,使用自旋锁
两个线程 枷锁线程比较长,使用互斥锁