场景
服务器有多个线程要写文件,若是一块儿写的话,就会发生顺序混乱,有可能刚刚写好了ABC还没来得及更新,就被后面的DEF覆盖掉。
解决方案
建立多个线程来读取服务器中的内容写到缓冲区,再建立一个线程专门读取缓冲区中的内容,缓冲区存满了就不能往里面写了,等读端读取部分数据
后通知写端能够往缓冲区中写入数据了,缓冲区空了就不能读取数据了,等待写端写入数据后通知它能够读了。
条件变量
1.定义条件变量 pthread_cond_t cond;
2.初始化 pthread_cond_init(&cond, NULL); // 第二个条件是属性
3.等待条件 pthread_cond_wait(&cond, &mutex);
// 若是mutex没有在互斥环境,就形同虚设,在互斥环境中wait函数将其置为1(1表示打开锁,无论原来的值是多少),
执行结束后,wait返回,mutex恢复成原来的值。
4.修改条件 pthread_cond_signal(&cond); // 激活等待条件,至关于一个通知
5.销毁条件变量 pthread_cond_destroy(&cond);
多个生产者往缓冲区中放入东西的时候,颇有可能放到同一块地方,所以要求生产者之间是互斥关系。同理消费者也是如此。
这就须要加上互斥锁来保证每次只有一个线程来操做。
生产者
int pthread_mutex_lock(pthread_mutex_t *mutex);
生产
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
消费者
int pthread_mutex_lock(pthread_mutex_t *mutex);
if (没有可消费)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
else
消费
int pthread_mutex_unlock(pthread_mutex_t *mutex);
分析:若是此时消费者发现没有能够消费的,因而就在等待生产者生产,可是此时生产者不能生产(ps:此时生产者因为加锁,进不去)。
因而就产生了死锁。而pthread_cond_wait的第二个参数是个互斥锁,能够将生产者的锁解开。
规范写法
pthread_mutex_lock();
while (条件不知足)
pthread_cond_wait();
pthread_mutex_unlock();
分析为何用while:这是由于wait函数是个阻塞函数,颇有可能被信号打断,而wait函数返回有两种状况,一种是被signal函数通知,
一种是被别的信号打断,设置成while后,若是是被信号打断,那么再次进入条件判断时,仍然是不知足的,不会执行下面的操做,
继续等待。这种信号打断的方式也叫做假唤醒。
pthread_mutex_lock();
pthread_cond_signal(); // 若是没有线程等待,也就是说条件一直知足,那么信号将被丢弃。
eg:假设条件是大于0,那么一开始大小为4时,知足条件,信号就不须要激活条件,执行完一次操做后,变成3仍然知足条件,信号
也会被丢弃。
pthread_mutex_unlock();
生产者-消费者模型
int *p = malloc(sizeof(int));
*p = i;
pthread_create(&tid[i], NULL, producer, p);
在线程中切记要释放掉开辟的堆内存
int id = *(int *)arg;
free(arg);
信号量 #include <semaphore.h>服务器
1.定义信号量 sem_t sem;
2.初始化信号量 int sem_init(sem_t *sem, int pshared, unsigned int value);
a. sem地址 b. 0表示本进程中多个线程间同步,非0表示能够跨进程的同步操做。c. 信号量初值(计数器的值)
3.PV操做
P int sem_wait(sem_t *sem); // sem-1 若是小于0就阻塞
V int sem_post(sem_t *sem); // sem+1
4.销毁 int sem_destroy(sem_t *sem);
仓库存放东西有限
生产者
while (1)
{
pthread_mutex_lock();
sem_wait(sem_full);
生产
sem_post(sem_empty);
pthread_mutex_unlock();
}
消费者
while (1)
{
pthread_mutex_lock();
sem_wait(sem_empty);
消费
sem_post(sem_full);
pthread_mutex_unlock();
}
场景:http服务器是基于短链接的,那么在网页上的访问就会频繁地建立和销毁线程,这样开销是很大的。
线程池(生产者消费者模型)
根据系统性能建立合适大小的线程池。
将建立的线程存入在线程池中,须要线程时,就让它们创建联系,不想用了,就断开联系。
1.线程池中有若干个线程
2.用于执行大量相对短暂的任务
计算密集型任务:大量的时间占用CPU进行运算。
线程池中线程的个数等于CPU个数,避免线程切换。
IO密集型任务:大量的时间占用CPU在阻塞等待IO。
线程池中线程个数大于CPU个数。
当任务增长时,能够动态增长线程池中线程的个数。当任务执行完成后,能够动态减小线程池中线程个数。
需求:
生产者线程向任务队列中添加任务,任务队列中有任务,若是线程池中有等待线程就唤醒它并执行任务,若是线程池中没有
等待线程,而且没有达到上限,就添加新的线程到线程池。
没有任务执行就等待,有任务则去执行。
typedef struct condition
{
pthread_mutex_t mutex;
pthread_cond_t cond;
}condition_t;
// 任务队列
typedef struct task
{
void *(pfun)(void *); // 任务队列的回调函数(管理任务的)
void *arg; // 回调函数传入的参数
struct task *next;
}task_t;
typedef struct threadpool
{
condition_t cond; // 同步与互斥
task_t *first; // 任务队列的队头
task_t *tail; // 任务队列的队尾
int max_thread; // 最大线程个数
int idle; // 空闲线程个数 若是有空闲线程,此时能够signal通知下
int counter; // 当前线程池中的线程个数
int quit; // 若是为1表示退出,为0表示不退出
}threadpool_t;
// 初始化
void threadpool_init(threadpool_t *pool, int max);
// 往线程池添加任务
void threadpool_add(threadpool_t *pool, void*(*pf)(void*), void *arg);
// 销毁线程池
void threadpool_destroy(threadpool_t *pool);
pthread_cond_broadcast(); // 唤醒全部
pthread_cond_signal(); // 只唤醒一个
---------------------
做者:sustzc
来源:CSDN
原文:https://blog.csdn.net/sustzc/article/details/82734994
版权声明:本文为博主原创文章,转载请附上博文连接!函数