欢迎光临
我们一直在努力

【Linux】线程之互斥(四)

文章目录

  • 🚩互斥背景概念
  • 🚩线程互斥
    • 🚩互斥量接口
      • 初始化互斥量
      • 互斥量的加锁与解锁
  • 实例
    • 🚩细节
      • 饥饿
  • 🚩锁的原理
  • 🚩RAII风格锁

🚩互斥背景概念

  • 临界资源:多线程执行流共享的资源叫临界资源,
  • 临界区:每个线程内部,访问临界资源的代码,叫临界区
  • 互斥:任何时刻, 只有一个执行流访问临界区,对临界资源起保护作用,
  • 原子性:只有两种状态,完成和未完成,没有正在完成

🚩线程互斥

为了防止上篇文章抢到负票情况,我们对临界区代码加锁,确保只有一个执行流访问临界区 互斥量就是实现加锁的一种工具 在这里插入图片描述

🚩互斥量接口

初始化互斥量

两种方式 1,静态分配

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

2,函数调用,动态分配

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 参数: mutex:要初始化的互斥量 attr:NULL

互斥量的加锁与解锁

int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值: 成功返回0,失败返回错误码

实例

int main()
{
pthread_mutex_t lock;
pthread_mutex_init(&lock,nullptr);
vector<pthread_t> tids;
vector<threadDate*> threadDates;
for(int i=1;i<=NUM;i++)
{
pthread_t tid;
threadDate* td=new threadDate(i,&lock);
threadDates.push_back(td);
pthread_create(&tid,nullptr,getTicket,threadDates[i1]);
tids.push_back(tid);
}
sleep(5);
for(auto tid:tids)
{
pthread_join(tid,nullptr);
}
for(auto td:threadDates)
{
delete td;
}
pthread_mutex_destroy(&lock)
return 0;
}

main的栈开辟空间存放lock,初始化lock, 怎么让每个线程看到同一把锁? 第一种:类中定义锁,将这把锁传递过去

class threadDate{
public:
threadDate(int number,pthread_mutex_t *mutex)
{
name="thread-"+to_string(number);
lock=mutex;
}
public:
string name;
pthread_mutex_t *lock;
};

第二种:定义全局锁

pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;

加锁的抢票系统,多个线程抢一把锁,抢到了向下执行,没抢到的线程被挂起,原地等待锁

void *getTicket(void* args)
{

threadDate* td=static_cast<threadDate*>(args);
const char* name=td->name.c_str();
while(1)
{
pthread_mutex_lock(td->lock);
if(ticket>0)
{
usleep(100);
printf("who:%s success,%d\\n",name,ticket);
ticket;
pthread_mutex_unlock(td->lock);
}
else{
pthread_mutex_unlock(td->lock);
break;
}
}
printf("who:%s is quit\\n",name);
return nullptr;
}

🚩细节

临界区被加锁了,可不可以被切换? 当然可以,但是锁在我这里,其他线程执行也是非临界区的代码

其他线程对待临界区就是原子状态,锁被释放或占用,

饥饿

在这里插入图片描述

但有一个问题,纯互斥场景会导致线程对锁竞争能力不同,比如我刚锁释放,其他线程还没被唤醒,我又抢到锁了,导致其他线程饥饿,但是某些场景用到纯互斥就用纯互斥 我们抢票不是纯互斥,只是互斥,因为抢了票了之后还要把票加载数据库等,

while(1)
{
pthread_mutex_lock(td->lock);
if(ticket>0)
{
usleep(100);
printf("who:%s success,%d\\n",name,ticket);
ticket;
pthread_mutex_unlock(td->lock);
}
else{
pthread_mutex_unlock(td->lock);
break;
}
usleep(10); //模拟抢票之后的动作
}

🚩锁的原理

锁的实现必须是原子的,不可能让多个线程同时获取锁 ticket–会汇编成3条语句,不是原子的, 一条汇编语句就是原子的 在这里插入图片描述

将0加载到al寄存器中,交换al和mutex值(原子性),判断如果寄存器值>0,申请锁成功,否则挂起等待,goto lock重新申请锁。锁被申请成功,其他线程带来自己的寄存器的值(0),无论怎么交换,都是0

交换的本质就是,把共享内存中的数据,与CPU寄存器al交换,把数据交给线程硬件上下文(私有的)带走,

把一个共享的锁,通过一条汇编交换到一个线程的上下文中,当前线程就持有锁了,

在这里插入图片描述 ==释放锁,把1加载到mutex中,==重新竞争

🚩RAII风格锁

把锁封装,

class Mutex{
public:
Mutex(pthread_mutex_t *lock)
:_lock(lock)
{}
void Lock()
{
pthread_mutex_lock(_lock);
}
void Unlock()
{
pthread_mutex_unlock(_lock);
}
~Mutex()
{}

public:
pthread_mutex_t *_lock;
};

class LockGuard{
public:
LockGuard(pthread_mutex_t *lock)
:_mutex(lock)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.Unlock();
}
private:
Mutex _mutex;
};

创建对象自动加锁了,析构函数释放锁

void *getTicket(void* args)
{

threadDate* td=static_cast<threadDate*>(args);
const char* name=td->name.c_str();
while(1)
{
//pthread_mutex_lock(td->lock);
//pthread_mutex_lock(&lock);
{
LockGuard lk(&lock);
if(ticket>0)
{
usleep(100);
printf("who:%s success,%d\\n",name,ticket);
ticket;
//pthread_mutex_unlock(td->lock);
//pthread_mutex_unlock(&lock);
}
else{
//pthread_mutex_unlock(td->lock);
//pthread_mutex_unlock(&lock);
break;
}
}
usleep(15);
}
printf("who:%s is quit\\n",name);
return nullptr;
}

额外的花括号,为对象创建独立的作用域, 出花括号对象销毁

赞(0)
未经允许不得转载:171主机测评 » 【Linux】线程之互斥(四)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址