小秦哥 发表于 2024-6-29 10:22:41

Linux--线程的互斥

线程系列:
一、线程的熟悉:线程的熟悉:误进解线程的概念和线程的基本控制
二、Linux–线程的分离、线程库的地点关系的明白、线程的简单封装
线程的互斥

线程互斥(Thread Mutual Exclusion)是多线程编程中的一个重要概念,它指的是在恣意时间只允许一个线程访问某一共享资源。这是为了克制多个线程同时访问和修改同一资源,从而导致数据不一致或其他不可预知的问题。
#include<iostream>
#include<vector>
#include"Thread.hpp"
#include<unistd.h>
#include<pthread.h>
using namespace ThreadMdule;
int g_tickets=1000;
class ThreadData
{
public:
    ThreadData(int& tickets,const string& name,pthread_mutex_t& mutex)
    :_tickets(tickets),_name(name),_total(0),_mutex(mutex)
    {

    }
    ~ThreadData(){}
public:
    int &_tickets;//所有线程都会引用到这个全局变量
    string _name;
    int _total;
    pthread_mutex_t& _mutex;
};
void route(ThreadData* td)
{
    while(true)
    {
      //pthread_mutex_lock(&gmutex);//加锁
      
      if(td->_tickets>0)
      {
            usleep(10000);
            printf("get tickets:%d\n",td->_tickets);
            td->_tickets--;
            //pthread_mutex_unlock(&gmutex);//过了临界区解锁
            td->_total++;
      }
      else
      {
            //pthread_mutex_unlock(&gmutex);
            break;
      }
    }
}

const int num=4;
int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,nullptr);

    vector<Thread<ThreadData*>> threads;
    vector<ThreadData*> datas;
    //创建新线程
    for(int i=0;i<num;i++)
    {
      string name="thread"+to_string(i + 1);
      ThreadData* td=new ThreadData(g_tickets,name,mutex);
      threads.emplace_back(route,td,name);
      datas.emplace_back(td);
    }
    //启动进程
    for(auto& thread:threads)
    {
      thread.start();
    }

    //等待进程结束
    for(auto& thread:threads)
    {
      thread.Join();
      cout<<"wait thread done,thread is: "<<thread.name()<<endl;
    }
    //统计数据
    for(auto data: datas)
    {
      cout<<data->_name<<" : "<<data->_total<<endl;
      delete data;
    }
    pthread_mutex_destroy(&mutex);
    return 0;
}
共享资源:每个线程可以同时访问的数据或其他资源。步伐给出1000张票,让线程来模拟抢票,
https://img-blog.csdnimg.cn/direct/22c315c0cbe748968f1f7462cbbd09ff.png
https://img-blog.csdnimg.cn/direct/2d7460d6470b4691ac518b545a547e35.png
总票数g_tickets会从内存中加载到CPU中进行逻辑运算,由于我们的线程没有进行掩护,也就是说当票数只有一张的时间,全部线程运行时没有时间先后,是并行运行的;
所以剩1张票时4个线程都在抢,京就会出现负数的情况;我们看到每个线程最后的抢票总数,也是相对平均的,说明是并行运行的;
如何解决互斥问题?

先说一个概念:
临界资源(Critical Resource)是指在多线程或多进程环境中,一次只能被一个线程或进程安全访问的资源。这种资源如果被多个实行流同时访问,可能会导致数据不一致、数据损坏或其他未定义的行为。
https://img-blog.csdnimg.cn/direct/e8ce55c2478944af8c638e69066c7cb1.png
临界资源通常是全局变量、共享数据结构(如链表、树、图等)、文件、网络连接、硬件装备或其他需要被多个实行线程或进程共享的资源。为了克制竞争条件和数据不一致,对临界资源的访问必须受到严格的控制。
为了掩护临界资源,步伐员通常会利用同步机制来确保在任何时间只有一个实行线程可以访问这些资源;
而利用互斥锁,就是实现同步机制的其中一种常见方法:
互斥锁(Mutex):一种简单的锁定机制,用于掩护临界资源。当一个线程拥有互斥锁时,其他试图获取该锁的线程将被壅闭,直到锁被释放。
互斥锁有关函数

在Linux中,线程互斥锁(Mutex)通常是通过POSIX线程库(pthread)来实现的。以下是与线程互斥锁相干的重要函数:

[*]pthread_mutex_init: 初始化互斥锁。
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);


[*]mutex:指向要初始化的互斥锁对象的指针。
[*]attr:指定互斥锁属性的对象,如果传递NULL,则利用默认的互斥锁属性。

[*]pthread_mutex_destroy: 销毁互斥锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);



[*]mutex:指向要锁定的互斥锁对象的指针。

[*]pthread_mutex_lock: 锁定互斥锁.
int pthread_mutex_lock(pthread_mutex_t *mutex);



[*]mutex:指向要锁定的互斥锁对象的指针。

[*]pthread_mutex_unlock: 解锁互斥锁。
`int pthread_mutex_unlock(pthread_mutex_t *mutex);


[*]mutex:指向要解锁的互斥锁对象的指针。

[*]pthread_mutex_trylock: 尝试锁定互斥锁,不会壅闭。
int pthread_mutex_trylock(pthread_mutex_t *mutex);


[*]mutex:指向要尝试锁定的互斥锁对象的指针
定义全局锁

https://img-blog.csdnimg.cn/direct/37f7cec9bb054956abba010f8a39444b.png
https://img-blog.csdnimg.cn/direct/4393f75524234e198f8f14bd9789b3f8.png
利用初始化和销毁来实现

https://img-blog.csdnimg.cn/direct/abf2a1223a2d437cb505837f5811d78e.png
https://img-blog.csdnimg.cn/direct/c8a84b88bfc54791aedfa95b0014db3f.png
线程互斥的底层实现

互斥锁是通过原子操作实现的。原子操作是不间断的单个指令,确保在实行过程中不被中断。
https://img-blog.csdnimg.cn/direct/d8ca7c380682499dbb329ada20ed5078.png
体系结构会提供一个swap或exchange的指令,该指令作用是将内存单元(mutex)和CPU上的寄存器进行数据交换,由于只有一条指令,包管了原子性。当某一个线程实行了这条指令,也就代表临界区被锁住了,那么只有当前这个线程能够过这个临界区,这就实现了对临界资源的掩护。
只需要通过一条指令即可实现互斥锁:这是由于当数据在内存中时,全部线程都是可以访问的,属于共享的。如果转移到CPU内部寄存器时,那么如许就变成了私有了!!例如thread1先实现了交换指令,那么对于thread1线程来说寄存器1是私有的,对于临界区仍然可以实行;thread2也想实现临界区的内容,但由于共享资源被锁住,寄存器2又没有交换后的内容,所以thread2无法实行临界区的内容,只能等待thread1解锁后才能得到互斥锁;

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Linux--线程的互斥