房地产市场低迷,上海哪学网站建设优化,深圳建站科技有限公司,国内精自线一二区网页版目录
前言#xff1a;基于多线程不安全并行抢票
一、线程互斥锁 mutex
1.1 加锁解锁处理多线程并发 1.2 如何看待锁
1.3 如何理解加锁解锁的本质
1.4 CRAII方格设计封装锁
前言#xff1a;基于线程安全的不合理竞争资源
二、线程同步
1.1 线程同步处理抢票
1.2 如何…目录
前言基于多线程不安全并行抢票
一、线程互斥锁 mutex
1.1 加锁解锁处理多线程并发 1.2 如何看待锁
1.3 如何理解加锁解锁的本质
1.4 CRAII方格设计封装锁
前言基于线程安全的不合理竞争资源
二、线程同步
1.1 线程同步处理抢票
1.2 如何理解条件变量
1.3 如何理解条件变量函数需要传锁参数 前言基于多线程不安全并行抢票
#includeiostream
#includepthread.h
#includeunistd.h
#define NUM 10
using namespace std;
int global_ticket 10000;void* GetTicket(void* args)
{char* pc (char*)args;while(global_ticket 0){usleep(123);cout pc ticket global_ticket--endl;}delete pc;
}int main()
{for(int i0;iNUM;i){char* pc new char[64];pthread_t tid;snprintf(pc,128,thread %d get a ticket,i1);pthread_create(tid,nullptr,GetTicket,pc);}while(true);return 0;
} 发现结果和我们代码的预想不一样出现了票已经售完却仍抢票的现象
为什么会出现这种现象 原来Linux线程是轻量级进程是CPU调度的基本单位所以每个线程在CPU中都有自己的时间片所以线程会在CPU上不断的切换这样有可能某个线程在运行函数过程中突然被截胡了然后再轮到它的时候继续运行那结合本次抢票逻辑可能某个线程首先while条件满足了拥有了抢票资格但是没有开始抢票就被截胡了下次回来后它仍拥有资格拿到一张票可是之前票已经被抢完了所以他其实没有资格了但是因为不安全访问全局变量使得它可以继续拿到票
这样因为线程的特性原因使得我们无法安全的访问一个全局共享变量所以我们要使线程互斥访问该变量当某一个线程访问的时候其他线程无法干预截胡该线程必须将自己的代码逻辑执行完或者不执行(要么不做做就必须完成) 这就叫线程的互斥那些代码区域称作为临界区 一、线程互斥锁 mutex
1.1 加锁解锁处理多线程并发 #includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includevector
using namespace std;int tickets 10000;class Thread_data
{
public:string name_;pthread_mutex_t* mutex_;
};void* pthread_route(void* args)
{Thread_data* pt static_castThread_data*(args);while(true){//加锁pthread_mutex_lock(pt-mutex_);if(tickets 0){cout pt-name_get a ticket, tickets --tickets endl;//解锁pthread_mutex_unlock(pt-mutex_);//!!!抢完票后的操作 如果没有这个步骤该线程会一直占用CPU执行抢票逻辑usleep(1234);}else{//注意这个逻辑里也要解锁pthread_mutex_unlock(pt-mutex_);break;}}delete pt;
}#define NUM 5
int main()
{vectorpthread_t vp(NUM); char buffer[64];//锁初始化//1.锁类型 初始化函数//2.全局变量 pthread_mutex_t mutex 宏(PTHREAD_MUTEX_INITIALIZER)pthread_mutex_t mutex;pthread_mutex_init(mutex,nullptr);for(int i0;iNUM;i){pthread_t tid;snprintf(buffer,sizeof(buffer),thread %d ,i1);Thread_data* pt new Thread_data();pt-mutex_ mutex;pt-name_ buffer;pthread_create((vp[i]),nullptr,pthread_route,pt);}for(const auto tid : vp){pthread_join(tid,nullptr);}return 0;
} 现在结果看到不会出现票数为负数的情况锁保护了我们的共享资源tickets 1.2 如何看待锁 a.锁本身被线程共享是一个共享资源锁保护全局变量锁本身也需要被保护 b.加锁和解锁的过程必须是安全的也就说加锁的过程必须是原子性的 c.如果锁申请成功就继续向后执行如果不成功则执行流阻塞 d.谁持有锁谁进入临界区 1.3 如何理解加锁解锁的本质
首先理解这个过程我们首先要明确关于CPU的一个知识就是寄存器
寄存器只有一套但寄存器被线程所共享寄存器存储的内容只被当前线程所拥有 加锁的过程
① 将内存中的线程变量al的值0放入CPU的寄存器内
② 然后将寄存器内的0与内存变量mutex值交换其中mutex的为1,这样mutex 0
③ 最后通过判断al在寄存器的值来判断是否能申请锁成功0 成功 else 不成功
此时当某个线程完成了前面两个步骤其他线程申请锁时将等于0的mutex与al交换al依旧是0所以他无法申请锁这样就可以保证只有一个线程申请到锁
解锁的过程就是把寄存器al的1存入mutex中即可
这里swap过程很重要它是一条汇编完成其本质是将共享变量放入到我们的上下文中 1.4 CRAII方格设计封装锁
//mutex.hpp
#includeiostream
#includepthread.hclass Mutex
{
public:Mutex(pthread_mutex_t* lock_p nullptr):lock_p_(lock_p){}void lock(){if(lock_p_)pthread_mutex_lock(lock_p_);}void unlock(){if(lock_p_)pthread_mutex_unlock(lock_p_);}
private:pthread_mutex_t* lock_p_;
};class LockGuard
{
public:LockGuard(pthread_mutex_t* mutex):mutex_(mutex){mutex_.lock();}~LockGuard(){mutex_.unlock();}
private:Mutex mutex_;
};
该封装利用局部类变量的构造析构随着作用域自动调用使得加锁解锁自动调用 前言基于线程安全的不合理竞争资源
#includeiostream
#includepthread.h
#includeunistd.h
#includestring
#includevector
#includemutex.hpp
using namespace std;int tickets 10000;class Thread_data
{
public:string name_;pthread_mutex_t* mutex_;
};void* pthread_route(void* args)
{Thread_data* pt static_castThread_data*(args);while(true){//加锁//pthread_mutex_lock(pt-mutex_);LockGuard lock(pt-mutex_);if(tickets 0){cout pt-name_get a ticket, tickets --tickets endl;//解锁//pthread_mutex_unlock(pt-mutex_);//!!!抢完票后的操作 如果没有这个步骤该线程会一直占用CPU执行抢票逻辑}else{//注意这个逻辑里也要解锁//pthread_mutex_unlock(pt-mutex_);break;}usleep(1000);}delete pt;
}#define NUM 5
int main()
{vectorpthread_t vp(NUM); char buffer[64];//锁初始化//1.锁类型 初始化函数//2.全局变量 pthread_mutex_t mutex 宏(PTHREAD_MUTEX_INITIALIZER)pthread_mutex_t mutex;pthread_mutex_init(mutex,nullptr);for(int i0;iNUM;i){pthread_t tid;snprintf(buffer,sizeof(buffer),thread %d ,i1);Thread_data* pt new Thread_data();pt-mutex_ mutex;pt-name_ buffer;pthread_create((vp[i]),nullptr,pthread_route,pt);}for(const auto tid : vp){pthread_join(tid,nullptr);}return 0;
} 可以肯定的是线程资源是安全的了但是却是一个线程在疯狂抢票这样的行为不是错误的但不合理我们需要每个线程都有机会所以引入线程同步利用同步信号与等待队列来实现线程公平的竞争 二、线程同步
1.1 线程同步处理抢票 #include iostream
#include pthread.h
#include unistd.h
#include string
using namespace std;int tickets 100;//初始化锁和条件变量
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond PTHREAD_COND_INITIALIZER;void *pthread_route(void *args)
{string name (char *)args;while (true){//加锁pthread_mutex_lock(mutex);//条件等待信号唤醒pthread_cond_wait(cond, mutex);if (tickets 0)cout name get a ticket, tickets --tickets endl;pthread_mutex_unlock(mutex);}
}int main()
{pthread_t tid1, tid2, tid3;pthread_create(tid1, nullptr, pthread_route, (void *)thread 1 );pthread_create(tid2, nullptr, pthread_route, (void *)thread 2 );pthread_create(tid3, nullptr, pthread_route, (void *)thread 3 );// 主线程负责每隔一秒信号唤醒条件变量while (true){sleep(1);pthread_cond_signal(cond);cout main wake up a thread ... endl;}pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);pthread_join(tid3, nullptr);return 0;
} 1.2 如何理解条件变量 首先明确一下整个代码逻辑 a.先加锁保护临界资源 b.条件等待等待条件满足信号唤醒否则阻塞 c.解锁 当线程条件阻塞会被放入条件队列中等待当有信号唤醒的时候从队列中一个一个得出
1.3 如何理解条件变量函数需要传锁参数 因为等待队列被线程共享为了保证线程入队列安全所以需要加锁保护线程安全