目录
Linux中的线程与进程
一、线程栈
1、线程自己的栈空间
线程库会被加载到进程地址空间中(共享区),tid为线程对象的起始地址。
多线程情况下测试局部变量test_i
#define NUM 5
struct threadData
{
string threadname;
};
string toHex(pthread_t tid)
{
char buffer[128];
snprintf(buffer,sizeof(buffer),"0x%x",tid);
return buffer;
}
void* threadRoutine(void* args)
{
threadData* td = static_cast<threadData*>(args);
int cnt=10;
int test_i=0;
while(cnt--)
{
cout<<"pid:"<<getpid()<<" tid:"<<toHex(pthread_self())
<<" test_i:"<<test_i++
<<" &test_i:"<<&test_i
<<" threadName:"<<td->threadname<<endl;
sleep(1);
}
delete td;
return nullptr;
}
void InitData(threadData* td,int number)//面向过程
{
td->threadname = "thread-" + to_string(number);
}
int main()
{
vector<pthread_t> tids;
for(int i=0;i<NUM;++i)
{
pthread_t tid;
threadData* td = new threadData;
InitData(td,i);
pthread_create(&tid,nullptr,threadRoutine,td);//create线程的时候写入tid
tids.push_back(tid);
sleep(1);
}
for(int i=0;i<tids.size();++i)
{
pthread_join(tids[i],nullptr);
}
return 0;
}
每个线程的test_i都是独立的,有自己的地址,是在线程各自的栈空间上开辟的。
堆空间是共享的,每个线程分配一块。
int *p = nullptr;定义一个全局的p变量
主线程中可以获取子线程的栈区上的局部变量。
也就是说,线程之间虽然有独立的栈区,但线程之间也是可以做到互相访问的。(在地址空间中)
但实际使用时,规定不能这样使用。
2、线程局部存储
int g_val1=0;
__thread编译选项,运用线程局部存储原理,在共享区上创建一个私有全局变量
只能创建内置类型
应用:可以保存一些需要系统调用的值(获取一些基本属性),提高效率。
__thread int g_val2=0;
二、线程分离
int pthread_detach(pthread_t thread);
pthread_detach(pthread_self());
for (auto i : tids)
{
pthread_detach(i);//主线程分离
}
for (int i = 0; i < tids.size(); i++)
{
int n = pthread_join(tids[i], nullptr);
printf("n = %d, who = 0x%x, why: %s\n", n, tids[i], strerror(n));
}
主线程调用pthread_exit只是退出主线程,并不会导致进程的退出
三、线程互斥
多线程并发抢票:
// -----------多线程并发抢票,互斥
#define NUM 4
int tickets = 1000;
class threadData
{
public:
threadData(int number)
{
threadName = "thread-" + to_string(number);
}
public:
string threadName;
};
string toHex(pthread_t tid)
{
char buffer[128];
snprintf(buffer, sizeof(buffer), "0x%x", tid);
return buffer;
}
void *getTicket(void *args)
{
threadData* td = static_cast<threadData*>(args);
while(tickets>0)
{
usleep(10000);
--tickets;
cout<<"threadname:"<<td->threadName<<" tid:"
<<toHex(pthread_self())<<" 剩余票数:"<<tickets<<endl;
}
cout<<"quit..."<<td->threadName<<endl;
return nullptr;
}
int main()
{
vector<pthread_t> tids;
vector<threadData*> thread_Datas;
for (int i = 1; i <= NUM; ++i)
{
pthread_t tid;
threadData* td = new threadData(i);
thread_Datas.push_back(td);
pthread_create(&tid, nullptr, getTicket, thread_Datas[i-1]);
tids.push_back(tid);
}
for(auto tid:tids)
{
pthread_join(tid,nullptr);
}
for(auto td:thread_Datas)
{
delete td;
}
return 0;
}
对一个全局变量多线程并发--
tickets--
的操作不是原子性的,而是分为三个步骤:
- 将共享变量tickets从内存加载到寄存器中
- 更新寄存器里面的值 执行-1操作
- 将新值从寄存器写回共享变量tickets的内存地址
例子:
假设现在有2个线程thread-1 和 thread-2进行抢票工作,分为上面的1,2,3步。
对于thread-1:执行第一步,从内存中读取到1000,1步完成。此时时间片到了,切换为thread-2
对于thread-2:时间片剩余较多,假设可以完整完成100次抢票工作。最后一次完成3步,此时eax中保存的值是900,最终将其写回内存中。然后切换回thread-1.
对于thread-1:继续第2步,先恢复上下文数据,将1000写到eax中,计算后为999,再进行第三步写回内存,此时内存中的值就变为999了,也就是多了100张票。
即对于thread2来说,tickets值前后不一致,即数据不一致问题。
tips:寄存器的内容!=寄存器
保存上下文到线程的对象内部,每次轮转到时恢复到CPU内的寄存器中
也就是thread-1认为自己一直在正确地--,实际上保存在上下文的那一份,拷贝回内存时,导致了最终的数据不一致问题。
数据不一致的原因:
tickets--的操作不是原子性的,即允许多个执行流同时进入,会互相干扰。
为避免该问题,则需要加锁操作。
为什么票数<=0时还能抢票呢?
判断tickets时,成立进入。
但设置了usleep,为了让多线程都停留在判断进入,但没有--操作,就被切换走了。
此时就会出现tickets为1,但有>1个线程判断成立。
后续的--操作就不判断tickets的值是否>0了
每次--都需要重新读取tickets的值,在这之前tickets可能已经被其它线程修改了。
四、互斥锁
1、init/destroy
pthread_mutex是库提供的一种数据类型
全局的mutex不用手动init和destroy
在main函数中创建并init一个lock。
在threadData中加入一个锁的指针,多线程对于一个临界区,共用一把锁。
2、lock/trylock/unlock
多线程共享的资源是临界资源,访问临界资源的代码叫临界区。
加锁本质:是用时间换安全。
加锁表现:原来多线程并发执行,对于加锁的临界区的代码变为串行执行。(并发度下降)
加锁原则:临界区的代码越少越好。
场景1:
lock和unlock之间的代码就是临界区。
问题1:
把lock和unlock放在while(1)外部会怎么样?
一直是一个线程在抢票(执行while(1)),与逻辑不符。
问题2:
lock失败则会阻塞等待,直到申请成功。
问题3:
tickets为0时,break跳出,不会unlock
在if和else中都要加unlock
锁的竞争性与饥饿问题:
上述代码加锁后,不会出现负数情况,但为什么还是一个线程在抢票?
这是由于多个线程对于锁的竞争性不同导致的。
对于lock的线程,unlock后可以直接继续lock,中间间隔很短,lock的概率大。
只要lock成功,即使轮转到别的线程,其它线程也只能阻塞等待。
对于其它线程,线程的切换需要的时间很长,lock的概率就很小。
在抢票完成后usleep,此时持有锁的进程不会立刻下一次lock,而是和其它线程一样进行时间片轮转,多线程直接lock的概率差距就变小了,即对锁的竞争性就差不多了。
饥饿问题:
解决方案--同步:
锁的申请和释放是原子性的:
联系信号量,PV操作的设计也是原子性(不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成)
锁是为了保护临界资源的,各线程申请一把锁,锁本身也是临界资源。
线程切换时,是持有锁一起切换走的,这期间其它线程不能进入临界区访问临界资源。该持有锁的线程访问临界区的过程,对其它线程来说是原子的。
实现原理:
为了实现互斥锁的操作,大多数体系结构都提供了swap
或exchange
指令,该指令的作用就是把寄存器和内存单元的数据相交换(一条汇编指令完成,即原子性的)
以加锁示例,这是由多条汇编语句执行的,上述
%al
是寄存器,mutex
就是内存中的一个变量。每个线程申请锁时都要执行上述语句,执行步骤如下:
- (
movb $0
,%al
)先将al寄存器中的值清0。该动作可以被多个线程同时执行,因为每个线程都有自己的一组寄存器(上下文信息),执行该动作本质上是将自己的al寄存器清0。注意:凡是在寄存器中的数据,全部都是线程的内部上下文!多个线程看起来同时在访问寄存器,但是互不影响。- (
xchgb %al
,mutex
)然后用此一条指令交换al寄存器和内存中mutex的值,xchgb是体系结构提供的交换指令,该指令可以完成寄存器和内存单元之间数据的交换。- 最后判断al寄存器中的值是否大于0。若大于0则申请锁成功,此时就可以进入临界区访问对应的临界资源;否则申请锁失败需要被挂起等待,直到锁被释放后再次竞争申请锁。
例子1:A-lock
现在线程A要开始加锁,执行上述语句。首先(movb $0
,%al
),线程A把0读进al
寄存器(清0寄存器)
然后执行第二条语句(xchgb %al
,mutex
)将al
寄存器中的值与内存中mutex
的值进行交换。
- 当线程A争议执行第三条语句if判断时,发生了线程切换(切至线程B),但是线程A要把自己的上下文(1)带走。线程B也要执行加锁动作,同样是第一条语句把0加载到寄存器,清0寄存器。
- 随后线程B执行第二条语句交换动作,可是
mutex
的数据先前已经被线程A交换至寄存器,然后保存到线程A的上下文了,现在的mutex
为0,而线程B执行交换动作,拿寄存器al
的0去换内存中mutex
的0。
最终A进行lock成功,B被挂起等待。
例子2:B-lock
线程A在执行第一条语句把寄存器清0后就发生了线程切换(切至线程B),线程A保存上下文数据(0),此时线程B执行第一条语句把0写进寄存器,随后线程B执行第二条语句xchgb
交换:
此时线程A执行第三条语句if判断失败,只能被挂起等待,线程A只能把自己的上下文数据保存,重新切换至线程B,也就是说线程B只要不运行,你们其它所有线程都无法申请成功。线程B恢复上下文数据(1)到寄存器,然后执行第三条语句if成功,返回结果。
交换的本质:上述
xchgb
就是申请锁的过程。申请锁是将数据从内存交换到寄存器,本质就是将数据从共享内存变成线程私有。
mutex
就是内存里的全局变量,被所有线程共享,但是一旦用一条汇编语句将内存的mutex
值交换到寄存器,寄存器内部是哪个线程使用,那么此mutex
就是哪个线程的上下文数据,那么就意味着交换成功后,其它任何一个线程都不可能再申请锁成功了,因为mutex
已经独属于某线程私有了。- 这个
mutex = 1
就如同令牌一般,哪个线程先交换拿到1,那么哪个线程就能申请锁成功,所以加锁是原子的。
unlock
进行unlock的一般都是是lock成功的那个线程,因此天然具有原子性。
某些情况下,也可以让其它线程进行unlock。
当线程释放锁时,需要执行以下步骤:
- 将内存中的
mutex
置回1。使得下一个申请锁的线程在执行交换指令后能够得到1,形象地说就是“将锁的钥匙放回去”。 - 唤醒等待
Mutex
的线程。唤醒这些因为申请锁失败而被挂起的线程,让它们继续竞争申请锁。
总结:
- 在申请锁时本质上就是哪一个线程先执行了交换指令,那么该线程就申请锁成功,因为此时该线程的al寄存器中的值就是1了。而交换指令就只是一条汇编指令,一个线程要么执行了交换指令,要么没有执行交换指令,所以申请锁的过程是原子的。
- 在线程释放锁时没有将当前线程
al
寄存器中的值清0,这不会造成影响,因为每次线程在申请锁时都会先将自己al
寄存器中的值清0,再执行交换指令。 - CPU内的寄存器不是被所有的线程共享的,每个线程都有自己的一组寄存器,但内存中的数据是各个线程共享的。申请锁实际就是,把内存中的
mutex
通过交换指令,原子性的交换到自己的al
寄存器中。
五、锁的应用
简单封装
class Mutex//封装Lock和Unlock接口
{
public:
Mutex(pthread_mutex_t* lock)
:_lock(lock){}
~Mutex(){}
void Lock()
{
pthread_mutex_lock(_lock);
}
void Unlock()
{
pthread_mutex_unlock(_lock);
}
private:
pthread_mutex_t* _lock;
};
class LockGuard
{
public:
LockGuard(pthread_mutex_t* lock)
:_mutex(lock)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.Unlock();
}
private:
Mutex _mutex;
};
使用一个全局的锁,且创建一个LockGuard设计RAII风格的锁
加一个代码块,让LockGuard完成RAII功能。
六、线程安全与可重入
七、死锁
2个线程2把锁的死锁场景
// int 票数计数器
int tickets = 1000; // 临界资源,可能会因为共同访问,造成数据不一致的问题
pthread_mutex_t Mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t Mutex1=PTHREAD_MUTEX_INITIALIZER;
void *getTickets(void *args)
{
const char *name = static_cast<const char *>(args);
while (true)
{
pthread_mutex_lock(&Mutex);
sleep(1);
pthread_mutex_lock(&Mutex1);
// 临界区
if (tickets > 0)
{
usleep(100);
cout << name << " 抢到了票, 票的编号: " << tickets << endl;
tickets--;
}else
{
break;
}
pthread_mutex_unlock(&Mutex1);
pthread_mutex_unlock(&Mutex);
}
return nullptr;
}
void *getTickets1(void *args)
{
const char *name = static_cast<const char *>(args);
while (true)
{
pthread_mutex_lock(&Mutex1);
sleep(1);
pthread_mutex_lock(&Mutex);
// 临界区
if (tickets > 0)
{
usleep(100);
cout << name << " 抢到了票, 票的编号: " << tickets << endl;
tickets--;
}else
{
break;
}
pthread_mutex_unlock(&Mutex);
pthread_mutex_unlock(&Mutex1);
}
return nullptr;
}
死锁四个必要条件:
解决死锁问题:
破坏4个必要条件中的一个即可。
1、互斥条件(编码):也就是不再使用锁,一般不会改变。
2、变为请求与不保持条件(接口)
例如A和B两个线程,A申请到了a锁,B申请到了b锁,然后A又要申请b锁,B又要申请a锁。
使用pthread_mutex_trylock接口,A申请b锁时,由于B持有锁,所以申请失败直接返回,if成功执行临界区代码,else失败会把a锁释放,然后再重新申请2把锁。
释放a锁后,B线程就可以正常申请到第二把a锁,使用完后释放a锁和b锁。
3、变为剥夺条件(接口)
线程A会因为没申请到b锁而阻塞,此时如果可以让B线程把b锁释放,A线程就可以申请成功了。
4、循环等待条件(编码)
2个线程需要2把锁,按照顺序申请锁,都是先申请a锁,再申请b锁
如:线程A申请到a锁,线程B申请a锁时会阻塞,此时b锁不在线程B上,线程A可以正常拿到b锁,然后执行完代码后依次释放a锁和b锁。
之后线程B才能申请这两把锁。
编码上避免死锁的原则
八、线程同步
同步概念
同步:在保证数据安全(有锁)的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。
如果没有锁,新来的线程都会直接访问临界资源,失败了才会排队,但访问期间就会导致线程不安全。
如果没有饥饿问题,只用互斥即可。
有饥饿问题,则需要同步。
简单实现同步(条件变量)
让阻塞的线程去排队(按一定顺序)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int cnt=0;
void* Cond(void* args)//condition
{
pthread_detach(pthread_self());
uint64_t number = (uint64_t)args;
cout<<"thread-"<<number<<" create success "<<endl;
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
cout<<"number:"<<number<<" cnt:"<<cnt++<<endl;
// sleep(3);
pthread_mutex_unlock(&mutex);
}
return nullptr;
}
int main()
{
for(uint64_t i=0;i<5;++i)
{
pthread_t tid;
pthread_create(&tid,nullptr,Cond,(void*)i);//把值作为指针传入,传值拷贝,使i不是共享资源,不要传地址&i
usleep(1000);
}
sleep(3);
cout<<"main thread ctrl begin"<<endl;
while(1)
{
sleep(1);
pthread_cond_signal(&cond);//唤醒cond等待队列中的第一个线程
cout<<"signal one thread"<<endl;
}
return 0;
}
九、生产消费者模型
BlockQueue
template <class T>
class BlockQueue
{
const static int defaultNum = 20;
private:
queue<T> q_;
int maxcap_;
// 保证生产者和消费者之间是互斥+同步的
pthread_mutex_t mutex_;// 互斥
pthread_cond_t c_cond_;// 同步
pthread_cond_t p_cond_;//分别放在2个不同的条件变量的队列中
int _high_water_ = maxcap_*2/3;//水位线控制策略
int _low_water_ = maxcap_/3;
public:
BlockQueue(int maxcap = defaultNum) : maxcap_(maxcap)
{
pthread_mutex_init(&mutex_, nullptr);
pthread_cond_init(&c_cond_, nullptr);
pthread_cond_init(&p_cond_, nullptr);
}
~BlockQueue()
{
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&c_cond_);
pthread_cond_destroy(&p_cond_);
}
void push(const T &in)
{
pthread_mutex_lock(&mutex_);
// 判断保证可以生产
if (q_.size() == maxcap_) // 判断时也要访问临界资源,需要放在加锁之后
{
pthread_cond_wait(&p_cond_, &mutex_); // 1、调度的时候自动释放锁
}
// 1、队列不满 2、满了后被唤醒,唤醒是因为已经有消费了
q_.push(in);
if(q_.size()>_high_water_)pthread_cond_signal(&c_cond_);
pthread_mutex_unlock(&mutex_);
}
const T pop()
{
pthread_mutex_lock(&mutex_);
// 判断保证可以消费
if (q_.size() == 0)
{
pthread_cond_wait(&c_cond_, &mutex_);
}
T out = q_.front();
q_.pop();
if(q_.size()<_low_water_)pthread_cond_signal(&p_cond_);
pthread_mutex_unlock(&mutex_);
return out;
}
};
demo-1
生产者和消费者都是单线程,传递int类型
#include"BlockQueue.hpp"
void* Productor(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
int data = 0;
while(1)
{
data++;
bq->push(data);
cout<<"生产了一个数据 "<<data<<endl;
sleep(1);
}
return nullptr;
}
void* Consumer(void* args)
{
BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
while (1)
{
int data = bq->pop();
cout<<"消费了一个数据 "<<data<<endl;
//sleep(1);
}
return nullptr;
}
int main()
{
BlockQueue<int>* bq = new BlockQueue<int>();
pthread_t c,p;
pthread_create(&c,nullptr,Consumer,bq);
pthread_create(&p,nullptr,Productor,bq);
pthread_join(c,nullptr);
pthread_join(p,nullptr);
delete bq;
return 0;
}
demo-2
生产者和消费者都是多线程,传递Task
#include "task.hpp"
#include"BlockQueue.hpp"
#define NUM_PRODUCTOR 10
#define NUM_CONSUMER 1
void *Consumer(void *args)
{
BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
while (true)
{
// 消费
Task t = bq->pop();
// 计算
// t.run();
t();
std::cout << "处理任务: " << t.GetTask() << " 运算结果是: " << t.GetResult() << " thread id: " << pthread_self() << std::endl;
// t.run();
// sleep(1);
}
}
void *Productor(void *args)
{
int len = opers.size();
BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
int x = 10;
int y = 20;
while (true)
{
// 模拟生产者生产数据
int data1 = rand() % 10 + 1; // [1,10]
usleep(10);
int data2 = rand() % 10;
char op = opers[rand() % len];
Task t(data1, data2, op);
// 只有与阻塞队列交互时才需要加锁
bq->push(t);
std::cout << "生产了一个任务: " << t.GetTask() << " thread id: " << pthread_self() << std::endl;
sleep(1);
}
}
int main()
{
srand(time(nullptr));
BlockQueue<Task>* bq = new BlockQueue<Task>();
pthread_t pdor[NUM_PRODUCTOR];
pthread_t cmer[NUM_CONSUMER];
for(int i=0;i<NUM_PRODUCTOR;++i)
{
pthread_create(&pdor[i],nullptr,Productor,bq);
usleep(100);
}
for(int i=0;i<NUM_CONSUMER;++i)
{
pthread_create(&cmer[i],nullptr,Consumer,bq);
usleep(100);
}
for(int i=0;i<NUM_PRODUCTOR;++i)
pthread_join(pdor[i],nullptr);
for(int i=0;i<NUM_CONSUMER;++i)
pthread_join(cmer[i],nullptr);
}
Task.hpp模拟任务
#pragma once
#include <iostream>
#include <string>
std::string opers="+-*/%";
enum{
DivZero=1,
ModZero,
Unknown
};
class Task
{
public:
Task(int x=0, int y=0, char op=opers[0]) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0)
{
}
void run()
{
switch (oper_)
{
case '+':
result_ = data1_ + data2_;
break;
case '-':
result_ = data1_ - data2_;
break;
case '*':
result_ = data1_ * data2_;
break;
case '/':
{
if(data2_ == 0) exitcode_ = DivZero;
else result_ = data1_ / data2_;
}
break;
case '%':
{
if(data2_ == 0) exitcode_ = ModZero;
else result_ = data1_ % data2_;
} break;
default:
exitcode_ = Unknown;
break;
}
}
void operator ()()
{
run();
}
std::string GetResult()
{
std::string r = std::to_string(data1_);
r += oper_;
r += std::to_string(data2_);
r += "=";
r += std::to_string(result_);
r += "[code: ";
r += std::to_string(exitcode_);
r += "]";
return r;
}
std::string GetTask()
{
std::string r = std::to_string(data1_);
r += oper_;
r += std::to_string(data2_);
r += "=?";
return r;
}
~Task()
{
}
private:
int data1_;
int data2_;
char oper_;
int result_;
int exitcode_;
};
RingQueue
#pragma once
#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <thread>
#include <stdio.h>
#include <vector>
#include <string.h>
#include <string>
#include <ctime>
#include <vector>
#include <semaphore.h>
using namespace std;
const int DEFAULT_CAPACITY = 5;
template <class T>
class RingQueue
{
private:
void P(sem_t &sem)
{
sem_wait(&sem);
}
void V(sem_t &sem)
{
sem_post(&sem);
}
void Lock(pthread_mutex_t &lock)
{
pthread_mutex_lock(&lock);
}
void Unlock(pthread_mutex_t &lock)
{
pthread_mutex_unlock(&lock);
}
public:
RingQueue(int cap = DEFAULT_CAPACITY)
: _cap(cap), _ring_queue(cap), c_step(0), p_step(0)
{
sem_init(&c_data, 0, 0);
sem_init(&p_space, 0, cap);
pthread_mutex_init(&p_lock, nullptr);
pthread_mutex_init(&c_lock, nullptr);
}
~RingQueue()
{
sem_destroy(&c_data);
sem_destroy(&p_space);
pthread_mutex_destroy(&c_lock);
pthread_mutex_destroy(&p_lock);
}
void Push(const T &in)
{
P(p_space);
Lock(p_lock);
_ring_queue[p_step] = in;
// 维持环形队列
p_step++;
p_step %= _cap;
Unlock(p_lock);
V(c_data);
}
void Pop(T *out)
{
P(c_data);
Lock(c_lock);
*out = _ring_queue[c_step];
// 维持环形队列
c_step++;
c_step %= _cap;
V(p_space);
Unlock(c_lock);
}
private:
std::vector<T> _ring_queue;
int _cap = 0;
int c_step = 0; // 消费者下标
int p_step = 0; // 生产者下标
sem_t c_data; // 消费者关注剩余数据
sem_t p_space; // 生产者关注剩余空间
pthread_mutex_t p_lock; // 生产者多线程间互斥访问环形队列
pthread_mutex_t c_lock; // 消费者多线程间互斥访问环形队列
};
demo-1
#include "RingQueue.hpp"
void *Productor(void *args)
{
RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
int i=0;
while (1)
{
cout << "product a data:" << i<<endl;
rq->Push(i);
++i;
//cout<<"6666"<<endl;
sleep(1);
}
}
void *Consumer(void *args)
{
RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
while (1)
{
sleep(1);
int data=-1;
rq->Pop(&data);
cout << "consumer a data:" <<data<< endl;
}
}
int main()
{
RingQueue<int> *rq = new RingQueue<int>();
pthread_t c, p;
pthread_create(&c, nullptr, Consumer, rq);
pthread_create(&p, nullptr, Productor, rq);
pthread_join(c, nullptr);
pthread_join(p, nullptr);
return 0;
}
demo-2
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include "RingQueue.hpp"
#include "Task.hpp"
using namespace std;
struct ThreadData
{
RingQueue<Task> *rq;
std::string threadname;
};
void *Productor(void *args)
{
// sleep(3);
ThreadData *td = static_cast<ThreadData*>(args);
RingQueue<Task> *rq = td->rq;
std::string name = td->threadname;
int len = opers.size();
while (true)
{
// 1. 获取数据
int data1 = rand() % 10 + 1;
usleep(10);
int data2 = rand() % 10;
char op = opers[rand() % len];
Task t(data1, data2, op);
// 2. 生产数据
rq->Push(t);
cout << "Productor task done, task is : "
<< t.GetTask() << " who: " << name << endl;
sleep(1);
}
return nullptr;
}
void *Consumer(void *args)
{
ThreadData *td = static_cast<ThreadData*>(args);
RingQueue<Task> *rq = td->rq;
std::string name = td->threadname;
while (true)
{
// 1. 消费数据
Task t;
rq->Pop(&t);
// 2. 处理数据
t();
cout << "Consumer get task, task is : "
<< t.GetTask() << " who: "
<< name << " result: " << t.GetResult() << endl;
// sleep(1);
}
return nullptr;
}
int main()
{
srand(time(nullptr) ^ getpid());
RingQueue<Task> *rq = new RingQueue<Task>(50);
pthread_t c[5], p[3];
for (int i = 0; i < 3; i++)
{
ThreadData *td = new ThreadData();
td->rq = rq;
td->threadname = "Productor-" + std::to_string(i);
pthread_create(p + i, nullptr, Productor, td);
}
for (int i = 0; i < 5; i++)
{
ThreadData *td = new ThreadData();
td->rq = rq;
td->threadname = "Consumer-" + std::to_string(i);
pthread_create(c + i, nullptr, Consumer, td);
}
for (int i = 0; i < 3; i++)
{
pthread_join(p[i], nullptr);
}
for (int i = 0; i < 5; i++)
{
pthread_join(c[i], nullptr);
}
return 0;
}