一个网站网站建设下来要花多少钱,电影网页设计素材,wordpress打开速度,江西赣州最新消息文章目录 ☀️一、线程同步#x1f33b;1.条件变量#x1f33b;2.同步概念与竞态条件#x1f33b;3.条件变量函数#x1f33b;4.条件变量使用规范#x1f33b;5.代码案例 ☀️二、生产者消费者模型#x1f33b;1.为何要使用生产者消费者模型#x1f33b;2.生产者消费者模… 文章目录 ☀️一、线程同步1.条件变量2.同步概念与竞态条件3.条件变量函数4.条件变量使用规范5.代码案例 ☀️二、生产者消费者模型1.为何要使用生产者消费者模型2.生产者消费者模型优点3.生产消费的关系 ☀️三、基于BlockingQueue的生产者消费者模型1.概念2.代码示例⚡1生产者给消费者派发整形数据简单版⚡2生产者给消费者派发任务复杂版 3.探究生产消费模型高校在哪里 ☀️一、线程同步
1.条件变量
当一个线程互斥地访问某个变量时它可能发现在其它线程改变状态之前它什么也做不了。例如一个线程访问队列时发现队列为空它只能等待只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。通过条件变量我们可以实现线程同步即可让线程顺序进行。我们访问临界资源的模式一般是这样的对临界资源加锁判断是否满足条件变量/生产消费条件解锁。
2.同步概念与竞态条件
同步在保证数据安全的前提下让线程能够按照某种特定的顺序访问临界资源从而有效避免饥饿问题叫做同步。竞态条件因为线程运行的时序问题而导致程序异常我们称之为竞态条件。
3.条件变量函数 1初始化
动态设置
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict
attr);
参数cond要初始化的条件变量attrNULL静态设置
pthread_cond_t cond PTHREAD_COND_INITIALIZER; //静态设置条件变量不用初始化、销毁2销毁
int pthread_cond_destroy(pthread_cond_t *cond)3等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数cond要在这个条件变量上等待mutex互斥量后面详细解释在新线程内部调用pthread_cond_wait可让线程加入等待队列中。 4唤醒等待
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);在新线程内部调用pthread_cond_signal就是把线程从等待队列中拿出来放到CPU中运行。
4.条件变量使用规范
等待条件代码 pthread_mutex_lock(mutex);while (条件为假pthread_cond_wait(cond, mutex);修改条件pthread_mutex_unlock(mutex);给条件发送信号代码 pthread_mutex_lock(mutex);设置条件为真pthread_cond_signal(cond);pthread_mutex_unlock(mutex);5.代码案例
makefile
testCond:testCond.ccg -o $ $^ -stdc11 -lpthread
.PHONY:clean
clean:rm -f testCondtestCond.cc
#include iostream
#include string
#include unistd.h
#include pthread.hint tickets 1000;
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond PTHREAD_COND_INITIALIZER; //静态设置条件变量不用初始化、销毁void* start_routine(void* args)
{std::string name static_castconst char*(args);while (true){pthread_mutex_lock(mutex);pthread_cond_wait(cond, mutex); //将新线程放入等待队列// wait函数参数为什么要有mutex为了后续释放和再次获取mutex锁//线程进入阻塞队列时要释放锁为了能让别的线程能访问该临界资源//线程被唤醒之后需要重新把锁申请回来//判断暂时省略std::cout name - tickets std::endl;tickets--;pthread_mutex_unlock(mutex);}
}int main()
{// 通过条件变量控制线程的执行pthread_t t[5];for (int i 0; i 5; i){char* name new char[64];snprintf(name, 64, thread %d, i 1);pthread_create(t i, nullptr, start_routine, name);}while (true){sleep(1); //#include unistd.h// pthread_cond_signal(cond); //每次循环唤醒一个新线程pthread_cond_broadcast(cond); //唤醒一批线程所有线程都会被唤醒std::cout main thread wakeup one thread... std::endl;}for (int i 0; i 5; i){pthread_join(t[i], nullptr);}return 0;
}运行结果 ☀️二、生产者消费者模型
1.为何要使用生产者消费者模型 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。 生产者和消费者彼此之间不直接通讯而通过阻塞队列来进行通讯所以生产者生产完数据之后不用等待消费者处理直接扔给阻塞队列消费者不找生产者要数据而是直接从阻塞队列里取阻塞队列就相当于一个缓冲区平衡了生产者和消费者的处理能力。 这个阻塞队列就是用来给生产者和消费者解耦的。 阻塞队列临时保存的数据场所在缓冲区中。
2.生产者消费者模型优点
解耦将生产和消费过程进行分离支持并发支持忙闲不均
3.生产消费的关系
生产者和生产者之间 - 互斥关系竞争关系。消费者和消费者之间 - 互斥关系。生产者和消费者之间 - 互斥 同步生产消费需要访问同一份资源时为互斥生产消费协同进行为同步。 生产消费模型巧记 - 321原则 3种关系生产者和生产者互斥消费者和消费者互斥生产者和消费者互斥(保证共享资源安全性) 同步 。2种角色生产者线程消费者线程。1个交易场所一段特定结构的缓冲区。生产消费的产品就是数据。 ☀️三、基于BlockingQueue的生产者消费者模型
1.概念 在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。 其与普通的队列区别在于当队列为空时从队列获取元素的操作将会被阻塞直到队列中被放入了元素 当队列满时往队列里存放元素的操作也会被阻塞直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的线程在对阻塞队列进程操作时会被阻塞)。 2.代码示例
⚡1生产者给消费者派发整形数据简单版
#include iostream
#include queue
#include stdlib.h
#include pthread.h#define NUM 8class BlockQueue {
private:std::queueint q;int cap;pthread_mutex_t lock;pthread_cond_t full;pthread_cond_t empty;private:void LockQueue(){pthread_mutex_lock(lock);}void UnLockQueue(){pthread_mutex_unlock(lock);}void ProductWait(){pthread_cond_wait(full, lock);}void ConsumeWait(){pthread_cond_wait(empty, lock);}void NotifyProduct(){pthread_cond_signal(full);}void NotifyConsume(){pthread_cond_signal(empty);}bool IsEmpty(){return (q.size() 0 ? true : false);}bool IsFull(){return (q.size() cap ? true : false);}public:BlockQueue(int _cap NUM) :cap(_cap){pthread_mutex_init(lock, NULL);pthread_cond_init(full, NULL);pthread_cond_init(empty, NULL);}void PushData(const int data){LockQueue();while (IsFull()) {NotifyConsume();std::cout queue full, notify consume data, product stop. std::endl;ProductWait();}q.push(data);// NotifyConsume();UnLockQueue();}void PopData(int data){LockQueue();while (IsEmpty()) {NotifyProduct();std::cout queue empty, notify product data, consume stop. std::endl;ConsumeWait();}data q.front();q.pop();// NotifyProduct();UnLockQueue();}~BlockQueue(){pthread_mutex_destroy(lock);pthread_cond_destroy(full);pthread_cond_destroy(empty);}
};void* consumer(void* arg)
{BlockQueue* bqp (BlockQueue*)arg;int data;for (; ; ) {bqp-PopData(data);std::cout Consume data done : data std::endl;}
}//more faster
void* producter(void* arg)
{BlockQueue* bqp (BlockQueue*)arg;srand((unsigned long)time(NULL));for (; ; ) {int data rand() % 1024;bqp-PushData(data);std::cout Prodoct data done: data std::endl;// sleep(1);}
}int main()
{BlockQueue bq;pthread_t c, p;pthread_create(c, NULL, consumer, (void*)bq);pthread_create(p, NULL, producter, (void*)bq);pthread_join(c, NULL);pthread_join(p, NULL);return 0;
}⚡2生产者给消费者派发任务复杂版
生产者派发任务计算任务 - 放入阻塞队列1 - 消费处理任务 - 放入阻塞队列2将结果储存 - 记录任务结果保存在文件中
BlockQueue.hpp
#include iostream
#include queue
#include pthread.hconst int gmaxcap 500;template class T
class BlockQueue
{
public:BlockQueue(const int maxcap gmaxcap) :_maxcap(maxcap){pthread_mutex_init(_mutex, nullptr);pthread_cond_init(_pcond, nullptr);pthread_cond_init(_ccond, nullptr);}void push(const T in) // 输入型参数const {pthread_mutex_lock(_mutex);// 1. 判断// 细节2: 充当条件判断的语法必须是while不能用ifwhile (is_full()) //bug?{// 细节1pthread_cond_wait这个函数的第二个参数必须是我们正在使用的互斥锁// a. pthread_cond_wait: 该函数调用的时候会以原子性的方式将锁释放并将自己挂起// b. pthread_cond_wait: 该函数在被唤醒返回的时候会自动的重新获取你传入的锁pthread_cond_wait(_pcond, _mutex); //因为生产条件不满足无法生产此时我们的生产者进行等待}// 2. 走到这里一定是没有满_q.push(in);// 3. 绝对能保证阻塞队列里面一定有数据// 细节3pthread_cond_signal这个函数可以放在临界区内部也可以放在外部pthread_cond_signal(_ccond); // 这里可以有一定的策略pthread_mutex_unlock(_mutex);//pthread_cond_signal(_ccond); // 这里可以有一定的策略}void pop(T* out) // 输出型参数* // 输入输出型{pthread_mutex_lock(_mutex);//1. 判断while (is_empty()) //bug?{pthread_cond_wait(_ccond, _mutex);}// 2. 走到这里我们能保证一定不为空*out _q.front();_q.pop();// 3. 绝对能保证阻塞队列里面至少有一个空的位置pthread_cond_signal(_pcond); // 这里可以有一定的策略pthread_mutex_unlock(_mutex);}~BlockQueue(){pthread_mutex_destroy(_mutex);pthread_cond_destroy(_pcond);pthread_cond_destroy(_ccond);}private:bool is_empty(){return _q.empty();}bool is_full(){return _q.size() _maxcap;}private:std::queueT _q;int _maxcap; // 队列中元素的上限pthread_mutex_t _mutex;pthread_cond_t _pcond; // 生产者对应的条件变量pthread_cond_t _ccond; // 消费者对应的条件变量
};Task.hpp
#include iostream
#include string
#include cstdio
#include functionalclass CalTask
{using func_t std::functionint(int, int, char);// typedef std::functionint(int,int) func_t;
public:CalTask(){}CalTask(int x, int y, char op, func_t func):_x(x), _y(y), _op(op), _callback(func){}std::string operator()(){int result _callback(_x, _y, _op);char buffer[1024];snprintf(buffer, sizeof buffer, %d %c %d %d, _x, _op, _y, result);return buffer;}std::string toTaskString(){char buffer[1024];snprintf(buffer, sizeof buffer, %d %c %d ?, _x, _op, _y);return buffer;}
private:int _x;int _y;char _op;func_t _callback;
};const std::string oper -*/%;int mymath(int x, int y, char op)
{int result 0;switch (op){case :result x y;break;case -:result x - y;break;case *:result x * y;break;case /:{if (y 0){std::cerr div zero error! std::endl;result -1;}elseresult x / y;}break;case %:{if (y 0){std::cerr mod zero error! std::endl;result -1;}elseresult x % y;}break;default:// do nothingbreak;}return result;
}class SaveTask
{typedef std::functionvoid(const std::string) func_t;
public:SaveTask(){}SaveTask(const std::string message, func_t func): _message(message), _func(func){}void operator()(){_func(_message);}
private:std::string _message;func_t _func;
};void Save(const std::string message)
{const std::string target ./log.txt;FILE* fp fopen(target.c_str(), a);if (!fp){std::cerr fopen error std::endl;return;}fputs(message.c_str(), fp);fputs(\n, fp);fclose(fp);
}MainCp.cc
#include BlockQueue.hpp
#include Task.hpp
#include sys/types.h
#include unistd.h
#include ctime//C计算
//S: 存储
templateclass C, class S
class BlockQueues
{
public:BlockQueueC *c_bq;BlockQueueS *s_bq;
};void *productor(void *bqs_)
{BlockQueueCalTask *bq (static_castBlockQueuesCalTask, SaveTask *(bqs_))-c_bq;// BlockQueueTask *bq static_castBlockQueueTask *(bq_);while (true){// sleep(3);// 生产活动,从数据库从网络从外设拿来的用户数据int x rand() % 100 1; // 在这里我们先用随机数构建一个数据int y rand() % 10;int operCode rand() % oper.size();CalTask t(x, y, oper[operCode], mymath);bq-push(t);std::cout productor thread, 生产计算任务: t.toTaskString() std::endl;}return nullptr;
}void *consumer(void *bqs_)
{BlockQueueCalTask *bq (static_castBlockQueuesCalTask, SaveTask *(bqs_))-c_bq;BlockQueueSaveTask *save_bq (static_castBlockQueuesCalTask, SaveTask *(bqs_))-s_bq;while (true){// 消费活动CalTask t;bq-pop(t);std::string result t(); // 任务非常耗时std::cout cal thread完成计算任务: result ... done std::endl;// SaveTask save(result, Save);// save_bq-push(save);// std::cout cal thread推送存储任务完成... std::endl; //sleep(1);}return nullptr;
}void *saver(void *bqs_)
{BlockQueueSaveTask *save_bq (static_castBlockQueuesCalTask, SaveTask *(bqs_))-s_bq;while(true){SaveTask t;save_bq-pop(t);t();std::cout save thread保存任务完成... std::endl; }return nullptr;
}int main()
{srand((unsigned long)time(nullptr) ^ getpid());BlockQueuesCalTask, SaveTask bqs;bqs.c_bq new BlockQueueCalTask();bqs.s_bq new BlockQueueSaveTask();pthread_t c[2], p[3], s;pthread_create(p, nullptr, productor, bqs);pthread_create(p1, nullptr, productor, bqs);pthread_create(p2, nullptr, productor, bqs);pthread_create(c, nullptr, consumer, bqs);pthread_create(c1, nullptr, consumer, bqs);pthread_create(s, nullptr, saver, bqs); //saver - 保存在文件pthread_join(c[0], nullptr);pthread_join(c[1], nullptr);pthread_join(p[0], nullptr);pthread_join(p[1], nullptr);pthread_join(p[2], nullptr);pthread_join(s, nullptr); //delete bqs.c_bq;delete bqs.s_bq;return 0;
}makefile
MainCp:MainCp.ccg -o $ $^ -stdc11 -lpthread
.PHONY:clean
clean:rm -f MainCp运行结果
3.探究生产消费模型高校在哪里
首先我们要清楚生产放入队列、消费拿出队列的动作是原子的。对于生产端我们构建一个任务可能十分耗时间构建完成之后可以竞争式的放进队列。简单来说就是每个线程之间的任务构造相互独立不需要一个一个任务串行构造可以并发式构造只有放进队列时要一个一个放进去节省了构造任务的时间。对于消费端只有将任务取出队列时要一个一个取而任务算法等可以并发式的实现节省了任务实现的时间。总结可以在生产之前和消费之后让线程并行执行。 多线3 的知识大概就讲到这里啦博主后续会继续更新更多C 和 Linux的相关知识干货满满如果觉得博主写的还不错的话希望各位小伙伴不要吝啬手中的三连哦你们的支持是博主坚持创作的动力