基础知识
1.互斥锁
- 初始化锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr); - 阻塞加锁
- int pthread_mutex_lock
- 非阻塞加锁
- 解锁
- int pthread_mutex_unlock
- 销毁,释放资源
== 互斥锁存在优先唤醒的问题==
2.条件变量
声明条件变量:pthread_cond_t cond;
声明条件变量并初始化:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER
- 初始化条件变量
- int pthread_cond_init()
- 阻塞等待
- int pthread_cond_wait()
- 超时等待
- int pthread_cond_timewaite()
- 唤醒一个等待该条件的线程
- int pthread_cond_signal()
- 唤醒全部等待该条件的线程
- int pthread_cond_broadcast()
- 销毁条件变量
- int pthread_cond_destroy()
3.条件变量和互斥锁关系
pthread_cond_wait函数发生:
- 释放互斥锁
- 等待条件
- 条件被触发–原子操作
- 给互斥锁加锁–原子操作
4.生产、消费者模型
代码实现1:
#include<stdio.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include<vector>
#include <pthread.h>
#include <signal.h>
using namespace std;
int mesgid=1;
struct st_message
{
int mesgid;
char message[1024];
}stmesg;
vector<struct st_message>vcache;//用容器做缓存
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//声明并初始化条件变量
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//声明并初始化互斥锁
{
struct st_message stmesg;
while(true)
{
pthread_mutex_lock(&mutex);
//如果缓存为空,等待
while(vcache.size()==0)
{
pthread_cond_wait(&cond,&mutex);
}
//从缓存中获取第一条记录,然后删除记录
memcpy(&stmesg,&vcache[0],sizeof(struct st_message));
vcache.erase(vcache.begin());
pthread_mutex_unlock(&mutex);
printf("phid=%ld,mesgid=%d\n",pthread_self(),stmesg.mesgid);
usleep(100);
}
}
//生产者把生产的数据存入缓存
void incache(int sig)
{
struct st_message stmesg;
memset(&stmesg,0,sizeof(stmesg));
pthread_mutex_lock(&mutex);//加锁
//生产数据,放入缓存
stmesg.mesgid=mesgid++;
vcache.push_back(stmesg);
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);//触发条件,激活全部线程
//pthread_cond_signal(&cond);//激活条件,激活排队中的第一个线程)
}
int main()
{
signal(15,incache);
pthread_t thid1,thid2,thid3;
pthread_create(&thid1,NULL,outcache,NULL);
pthread_create(&thid2,NULL,outcache,NULL);
pthread_create(&thid3,NULL,outcache,NULL);
pthread_join(thid1,NULL);
pthread_join(thid2,NULL);
pthread_join(thid3,NULL);
return 0;
}
代码实现2:
多生产者,多消费者
//多生产者多消费者模型
#include <unistd.h>
#include<stdio.h>
#define M 8
#define N 10
#include<semaphore.h>
#include <pthread.h>
//定义三个信号量,缓冲区大小,产品
sem_t space,prod,buf;
int Buffer[N];
int in=0;
int out=0;
void* producer(void* p);
void* consumer(void* p);
int main(void)
{
sem_init(&space,0,N);
sem_init(&prod,0,0);
sem_init(&buf,0,1);
pthread_t tid1[M],tid2[M];
for(int i=0;i<M;i++)
{
pthread_create(&tid1[i],NULL,producer,NULL);
}
for(int i=0;i<M;i++)
{
pthread_create(&tid2[i],NULL,consumer,NULL);
}
sleep(20);
sem_destroy(&space);
sem_destroy(&prod);
sem_destroy(&buf);
return 0;
}
void* producer(void* p)
{
while(1)
{
sem_wait(&space);
sem_wait(&buf);
printf("Put a product into Buf[%d]\n",in);
in=(in+1)%N;
sem_post(&prod);
sem_post(&buf);
}
pthread_exit(0);
return NULL;
}
void* consumer(void* p)
{
while(1)
{
sem_wait(&prod);
sem_wait(&buf);
printf("Get a product from Buf[%d]\n",out);
out=(out+1)%N;
sem_post(&space);
sem_post(&buf);
}
pthread_exit(0);
return NULL;
}
运行窗口:
5.信号量
- 数值表示空闲临界资源的数量
- 初始化信号量 int sem_init
- 等待信号量:信号量值为0则等待
- int sem_wait
- 释放信号量:信号量加1
- int sem_post
- 获取信号量
- int sem_getvalue
- 销毁信号量,释放资源
- int sem_destroy
6.信号量实现高速缓存
信号量可以和互斥锁实现相同的功能
sem_wait(&sem)
sem_post(&sem)
pthread_mutex_lock(&mutex)
pthread_mutex_unlock(&mutex)
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include<vector>
#include <pthread.h>
#include <signal.h>
using namespace std;
int mesgid=1;
struct st_message
{
int mesgid;
char message[1024];
}stmesg;
//容器做高速缓存
vector<struct st_message>vcache;//用容器做缓存
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//声明并初始化条件变量
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//声明并初始化互斥锁
sem_t sem;//声明信号量
void *outcache(void *arg)
{
struct st_message stmesg;
while(true)
{
while(vcache.size()==0)
{
sem_wait(&sem);
}
pthread_mutex_lock(&mutex);
//再次进行判断,保证确实是有资源的,此操作非原子操作
if(vcache.size()==0)
{
pthread_mutex_unlock(&mutex);
continue;
}
//如果缓存为空,等待
//从缓存中获取第一条记录,然后删除记录
memcpy(&stmesg,&vcache[0],sizeof(struct st_message));
vcache.erase(vcache.begin());
pthread_mutex_unlock(&mutex);
printf("phid=%ld,mesgid=%d\n",pthread_self(),stmesg.mesgid);
//如果不sleep,则会被进程一直抢占资源
usleep(100);
}
}
//生产者把生产的数据存入缓存
void incache(int sig)
{
struct st_message stmesg;
memset(&stmesg,0,sizeof(stmesg));
pthread_mutex_lock(&mutex);//加锁
//生产数据,放入缓存
stmesg.mesgid=mesgid++;
vcache.push_back(stmesg);
pthread_mutex_unlock(&mutex);
sem_post(&sem);//信号量加1
//pthread_cond_signal(&cond);//激活条件,激活排队中的第一个线程)
}
int main()
{
signal(15,incache);
sem_init(&sem,0,1);//初始化信号量为1
pthread_t thid1,thid2,thid3;
pthread_create(&thid1,NULL,outcache,NULL);
pthread_create(&thid2,NULL,outcache,NULL);
pthread_create(&thid3,NULL,outcache,NULL);
pthread_join(thid1,NULL);
pthread_join(thid2,NULL);
pthread_join(thid3,NULL);
return 0;
}
7.自旋锁
定义:自旋锁已经被占用,调用者就会一直在那里判断占用者是否已经释放了锁,用于linux内核同步
函数:
- 初始化锁
- int pthread_spin_init
- 阻塞加锁,若互斥锁被其他线程上锁,调用者一直等待,直到被解锁后才上锁
- int pthread_spin_lock
- 非阻塞加锁,若互斥锁未加锁,则上锁,若互斥锁已加锁,则函数立即返回失败
- int pthread_spin_trylock
- 解锁
- int pthread_spin_unlock
- 销毁锁
- int pthread_spin_destroy
8.读写锁
读写锁的特点
- 如果线程申请了读锁,则其他线程可以申请读锁,但不能申请写锁
- 如果线程申请了写锁,其他线程既不能申请读锁,也不能申请写锁
- 函数
- 初始化读写锁
- int pthread_rwlock_init
- 申请读锁
- int pthread_rwlock_rdlock
- 申请写锁
- int pthread_rwlock_wrlock
- 尝试申请写锁,如果有线程持有读锁或者写锁,返回失败
- int pthread_rwlock_trywrlock
- 解锁
- int pthread_rwlock_unlock
- 销毁读写锁
- int pthread_rwlock_destroy
客户端 ------数据库-------缓存-------磁盘
9.读者-写者&哲学家进餐问题