前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Linux】详解加锁实现线程互斥

【Linux】详解加锁实现线程互斥

作者头像
用户10923276
发布2024-08-06 08:24:44
1020
发布2024-08-06 08:24:44
举报
文章被收录于专栏:北飞的山羊知识库

一、多线程不加线程互斥可能会引发的问题

        下面是一个抢标逻辑。抢票为什么会抢到负数:假设当票数为1时,此时四个进程的判断条件tickets都大于0,都会进入抢票操作,第一个进程抢完票以后tickets==0并写回内存,第二个进程再从内存中读取tickets的值时此时tickets已经为0,再做--就变成了-1,tickets为负数就是这么来的。也就是说,多线程代码如果不对共享资源做保护可能会有并发问题。

二、互斥锁

2.1、静态分配锁

如果你定义的锁是静态的或者是全局的,可以直接初始化成

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

2.2、动态分配锁销毁锁

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

参数mutex:要初始化的互斥量,attr: NULL。

int pthread_mutex_destroy(pthread_mutex_t *mutex) ;

注意: 使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁

2.3、加锁解锁

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

返回值:成功返回0,失败返回错误号 。 pthread_mutex_lock函数如果申请锁成功就会继续向后运行,如果申请失败该函数就会阻塞不允许继续向后运行。

加锁的粒度要越细越好

三、加锁的底层理解

movb $0,%al表示将0存入%al寄存器中(%al是累加寄存器AX的低8位部分,可以独立作为8位寄存器使用。), xchgb %al, mutex表示交换%al寄存器中的值和内存mutex中的值,如果内存mutex中的值原本是1,交换完则表示得到锁,否则挂起等待。unlock中将1存入mutex内存中表示归还锁。这样无论如何,得到1的线程始终只会有一个,也就做到了线程互斥。

 四、多线程实现简单的互斥抢票

代码语言:javascript
复制
//thread.hpp
#ifndef __THREAD_HPP__
#define __THREAD_HPP__

#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>

namespace ThreadModule
{
    template<typename T>
    using func_t = std::function<void(T)>;
    // typedef std::function<void(const T&)> func_t;

    template<typename T>
    class Thread
    {
    public:
        void Excute()
        {
            _func(_data);
        }
    public:
        Thread(func_t<T> func, T data, const std::string &name="none-name")
            : _func(func), _data(data), _threadname(name), _stop(true)
        {}
        //记住此方法
        static void *threadroutine(void *args) // 类成员函数,形参是有this指针的!!
        {
            Thread<T> *self = static_cast<Thread<T> *>(args);
            self->Excute();
            return nullptr;
        }
        bool Start()
        {
            int n = pthread_create(&_tid, nullptr, threadroutine, this);
            if(!n)
            {
                _stop = false;
                return true;
            }
            else
            {
                return false;
            }
        }
        void Detach()
        {
            if(!_stop)
            {
                pthread_detach(_tid);
            }
        }
        void Join()
        {
            if(!_stop)
            {
                pthread_join(_tid, nullptr);
            }
        }
        std::string name()
        {
            return _threadname;
        }
        void Stop()
        {
            _stop = true;
        }
        ~Thread() {}

    private:
        pthread_t _tid;
        std::string _threadname;
        T _data;
        func_t<T> _func;
        bool _stop;
    };
} 

#endif
代码语言:javascript
复制
//LockGuard.hpp
#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__

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

class LockGuard
{
private:
    pthread_mutex_t* _mutex;
public:
    LockGuard( pthread_mutex_t* mutex)
    :_mutex(mutex)
    {
        pthread_mutex_lock(_mutex);
    }
    ~LockGuard()
    {
        pthread_mutex_unlock(_mutex);
    }

};

#endif
代码语言:javascript
复制
//testThread.cc
#include <iostream>
#include <vector>
#include "LockGuard.hpp"
#include "Thread.hpp"
using namespace ThreadModule;

int g_tickets = 10000;
const int num = 4;

class ThreadData
{
public:
    int &_tickets; // 所有的线程,最后都会引用同一个全局的g_tickets
    std::string _name;
    int _total;
    pthread_mutex_t &_mutex;

public:
    ThreadData(int &tickets, const std::string &name, pthread_mutex_t &mutex)
        : _tickets(tickets), _name(name), _total(0), _mutex(mutex)
    {
    }
    ~ThreadData()
    {
    }
};

void route(ThreadData *td)
{
    while (true)
    {
        LockGuard guard(&td->_mutex);
        if (td->_tickets > 0)
        {
            usleep(1000);
            printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_tickets); 
            td->_tickets--;                                                          
            td->_total++;
        }
        else
            break;
    }
}



int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,nullptr);

    std::vector<Thread<ThreadData*>> threads;
    std::vector<ThreadData *> datas;

    //1、创建一批线程
    for(int i = 0; i<num; i++)
    {
        std::string name = "thread-" + std::to_string(i+1);
        ThreadData* td = new ThreadData(g_tickets, name, mutex);
        threads.emplace_back(route, td, name);
        datas.emplace_back(td);
    }

    // 2. 启动 一批线程
    for (auto &thread : threads)
    {
        thread.Start();
    }

    // 3. 等待一批线程
    for (auto &thread : threads)
    {
        thread.Join();
    }
    sleep(1);
    // 4. 输出统计数据
    for (auto data : datas)
    {
        std::cout << data->_name << " : " << data->_total << std::endl;
        delete data;
    }

    pthread_mutex_destroy(&mutex);
    
    return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-08-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、多线程不加线程互斥可能会引发的问题
  • 二、互斥锁
    • 2.1、静态分配锁
      • 2.2、动态分配锁销毁锁
        • 2.3、加锁解锁
        • 三、加锁的底层理解
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档