Linux入门之多线程|线程的互斥|锁|封装线程|封装锁|死锁

文章目录

一、线程互斥

1.概念

二、线程互斥接口

1.互斥量的接口

初始化互斥量

互斥量的的加锁和解锁

销毁互斥量

2.互斥量的原理

三、线程的封装

四、锁的封装

五、死锁

1.死锁的概念

2.产生死锁的必要条件:

3.避免死锁:核心思想 破环死锁的4个必要条件的任意一个

总结



多线程中有一个全局变量,是被所有执行流共享的。线程中,大部分资源都会直接或间接共享。只要存在共享,就可能存在并发访问的问题,进而导致数据不一致问题。

一、线程互斥

1.概念

  • 临界资源:多线程执行流共享的资源就叫做临界资源(对共享资源进行一定的保护)
  • 临界区:任何一个线程,都有代码,访问临界资源。线程中的访问临界资源的代码称为临界区。
  • 非临界区:线程中不访问临界资源的代码称为非临界区。
  • 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用。
  • 原子性:不会被任何调度机制打断的操作,该操作只有两种状态,要么完成,要么未完成

二、线程互斥接口

1.互斥量的接口

初始化互斥量

  1. 静态分配 (静态分配的锁不需要被销毁)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

     2.动态分配

int pthread_mutex_init(phthread_mutex_t * restrict mutex, const pthread_mutexattr_t * restict attr);

参数:mutex 要初始化的互斥量
     attr:null

互斥量的的加锁和解锁

int pthread_mutex_lock(pthread_mutex_t * mutex);
int pthread_mutex_unlock(pthread_mutex_t * mutex);
//成功返回0,失败返回错误码

  • 调用pthread_lock时候,可能有两种情况:互斥量处于未锁状态,该函数将互斥量锁定,同时返回成功

  • 发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但是没有竞争到互斥量,那么pthread_lock将会阻塞(执行流被挂起),等待互斥量解锁。

销毁互斥量

不要销毁一个已经加锁的互斥量

已经销毁的互斥量,要确保后面不会有线程再尝试加锁

int pthread_mutex_destroy(pthread_mutex_t * mutex);

2.互斥量的原理

          为了实现互斥锁的操作,大多数体系结构提供了swap或者exchange指令,该指令的作用是把寄存器和内存单元的数据进行交换。由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的总线周期也有先后,一个处理器上的交换指令执行时,另一个处理器的交换指令只能等待总线周期。

        共享资源是要存储到内存中,mutex再内存中,起始mutex = 1,有两个线程A和B,调度器先执行线程A的工作,A将寄存器中的值先初始化为0,然后A将内存和cpu中的数据进行交换,此时寄存器中的值为1,内存中的值为0,A获得了这把锁。时间片到了,B开始执行,B将寄存器中的值改为0,自己去执行交换的时候内存里mutex = 0,此时线程被挂起。

三、线程的封装

class Thread
{
   public:
    typedef enum
    {
        NEW = 0,
        RUNNING,
        EXITED
    }ThreadStatus;

    typedef void(*func_t)(void *);
    
    //构造
    Thread(int num,func_t fun,void * args)
    :_tid(0),_status(NEW),_func(func),_args(args)
    {
        char name[128];
        snprintf(name,sizeof(name),"thread - % d",num);
        _name = name;
    }

    int status() { return _status;}
    std::string threadname() {return _name;}
    pthread_t thread_id()
    {
        if(_status == RUNNING) return _tid;
        else return 0;
    }

    static void * runHelper(void * args)
    {
        Thread * ts = (Thread * )args; // 拿到了当前对象
        (*ts)();
        return nullptr;
    }


    void operator()()
    {
        if(_func!= nullptr) _func(_args);
    }

    //创建线程
    void run()
    {
        int n = pthread_create(&_tid,nullptr,runHelper,this);
        if( n!= 0) exit(1);
        _status = RUNNING;
    }

    void join()
    {
        if n = pthread_join(_tid,nullptr);
        if(n!= 0) return ;
        status = EXITED;
    }

    ~Thread()
    {}



    private:
        pthread_t _tid;
        std::string _name;
        func_t _func; //线程未来要执行的回调
        void * args;
};

四、锁的封装

#pragma once 

#include<iostream>
#include<pthread.h>

int tickets = 1000; //抢票 共享资源

pthread_mutex_mutex =   PTHREAD_MUTEX_INITIALIZER;


class Mutex   //自己不维护锁,由外部传入
{
public:
    Mutex(pthread_mutex_t * mutex)
    :_pmutex(mutex)
    {}
    
    void lock()
    {
        pthread_mutex_lock(_pmutex);
    }

    void unlock()
    {
        pthread_mutex_unlock(_pmutex);
    }

    ~Mutex()
    {}

   private:
    pthread_mutex_t * _pmutex;
  
};


class LockGuard
{   public:
        LockGuard(pthread_mutex_t * mutex)
        :_mutex(mutex)
    {
        _mutex.lock();
    }

    ~LockGuard()
    {
        _mutex.unlock();
    }


    private:
        Mutex _mutex;
}; 


void threadRoutine(void * args)
{
    std::string message = static_cast<const char *>(args);
    while(true)
    {
        LockGuard lockguard(&mutex);
        if(ticktes > 0)
        {
            usleep(200);
            cout<<message<<"get a ticket: "<<tickets --<<endl; //临界区
        }

        else
        {
            break;
        }
    }

    //抢完票后续的动作放入用户数据库中
    usleep(1000);
    LockGuard lockguard(&mutex);
}

int main()
{
    Thread t1(1, threadRoutine, (void *)"1");
    Thread t2(2, threadRoutine, (void *)"2");
    Thread t3(3, threadRoutine, (void *)"3");
    Thread t4(4, threadRoutine, (void *)"4");

   
    return 0;
}

五、死锁

多线程代码存在并发访问临界资源的问题,所以产生了加锁的策略。加锁之后可能导致死锁。

1.死锁的概念

2.产生死锁的必要条件:

1.互斥条件:一个资源每次只能被一个执行流使用

2.请求与保持:一个执行流因请求资源而阻塞,对已获得的资源保持不妨

3.环路等待(循环等待条件):若干执行流之间形成一种头尾相接的循环等待资源的关系

4.不剥夺条件:一个执行流已获得的资源,在未使用完之前,不能强行剥夺

3.避免死锁:核心思想 破环死锁的4个必要条件的任意一个

解决死锁问题:1.不加锁 2.主动释放锁 3.按照顺序申请锁 4.控制线程统一释放锁


总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值