【Linux —— 线程互斥】

news2025/1/9 1:10:26

Linux —— 线程互斥

  • 1. 临界资源与临界区
  • 2. 互斥的定义
  • 3. 原子性
  • 4. 互斥量(Mutex)
  • 5. 互斥的实现示例

1. 临界资源与临界区

  • 临界资源: 指的是多个线程或进程共享的资源,例如全局变量、文件、数据库等。由于这些资源的共享,可能会导致数据不一致或程序崩溃。
  • 临界区: 是指访问临界资源的代码段。 为了保护临界资源,必须控制对临界区的访问,确保在任何时刻只有一个线程或进程可以进入临界区。

2. 互斥的定义

 互斥是一种同步机制,旨在确保同一时刻只有一个执行流(线程或者进程)可以进入临界区。
互斥通常通过互斥量(mutex)来实现。 互斥量是一种锁,线程或进程在访问临界资源之前需要获取这个锁,完成后释放它。

3. 原子性

 原子性通俗讲就是指某个操作要么完全执行,要么就完全不执行,不会被其他的进程或线程打断。
原子性操作对于保证数据的一致性和安全性至关重要。比如:在抢票软件的抢票过程中,如果没有原子性则可能出现售出的票数大于总票数的情况。

4. 互斥量(Mutex)

  • 互斥量的使用:
    • 初始化: 只是用互斥量之前,必须对其进行初始化。可以使用pthread_mutex_init 函数来进行初始化。
    • 加锁: 线程或进程在进入临界区之前,需要调用pthread_mutex_lock 来获取互斥量的锁。如果锁已经被其他的线程占用了,当前线程将被阻塞,知道锁被释放。
    • 解锁: 完成对临界资源的访问之后,必须调用pthread_mutex_unlock 来释放互斥量的锁。以允许其他线程访问临界区。
    • 销毁: 在不再需要互斥量的时候,应该调用pthread_mutex_destroy来销毁互斥量,释放有关的资源。

5. 互斥的实现示例

下面简单使用自己封装的pthread库来进行演示:

//Thread.hpp
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>

namespace ThreadMoudle
{
    typedef void (*func_t)(const std::string &name); // 函数指针

    class Thread
    {
    public:
        void Excute()
        {
            std::cout << _name << " is running ..." << std::endl;
            _isrunning = true;
            _func(_name);
            _isrunning = false;
        }

    public:
        Thread(std::string name, func_t func)
            : _name(name), _func(func)
        {
            std::cout << "create " << name << " done ..." << std::endl;
        }

        static void *ThreadRountine(void *args)
        {
            Thread *self = static_cast<Thread *>(args);
            self->Excute();
            return nullptr;
        }
        bool Start()
        {
            int n = ::pthread_create(&_tid, nullptr, ThreadRountine, this);
            if (n != 0)
            {
                return false;
            }
            return true;
        }
        std::string Status()
        {
            if (_isrunning)
            {
                return "running...";
            }
            else
            {
                return "sleep...";
            }
        }

        void Stop()
        {
            if (_isrunning)
            {
                ::pthread_cancel(_tid);
                _isrunning = false;
                std::cout << _name << " Stop..." << std::endl;
            }
        }

        void Join()
        {
            ::pthread_join(_tid, nullptr);
            std::cout << _name << " Joined..." << std::endl;
        }

        std::string Name()
        {
            return _name;
        }

        ~Thread()
        {
        }

    private:
        std::string _name;
        pthread_t _tid;
        bool _isrunning;
        func_t _func; // 回调函数
    };
}

简单写一个抢票代码,总共有10000张票,创建出4个线程来同时抢票:

#include <iostream>
#include <vector>
#include <cstdio>
#include <unistd.h>
#include "Thread.hpp"

using namespace ThreadMoudle;

static int ticket = 10000;

void route(const std::string &name)
{
    while(true)
    {
        if(ticket > 0)
        {
            usleep(1000);
            printf("%s get a ticket: %d \n ",name.c_str(),ticket);
            ticket--;
        }
        else
        {
            break;
        }
    }
}

int main ()
{
    Thread t1("thread-1", route);
    Thread t2("thread-2", route);
    Thread t3("thread-3", route);
    Thread t4("thread-4", route);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();


    return 0;
}

在这里插入图片描述

我们可以看到出现了抢到负数的情况。这是为什么呢?


  • 这是因为在多线程环境中,操作系统会在多个线程之间进行切换和调度,每个线程都有其自己的程序计数器和寄存器,用于记录当前执行的位置和状态。操作系统会通过某种调度算法来决定哪个线程获得CPU时间片。
  • 然而当多个线程访问同一个共享资源的时候,他们共同会读取和修改该资源。
    假设有两个线程A和B,他们都要访问ticket变量。线程A和B的执行过程如下:
  1. 线程A获取ticket的值,发现是1,同时线程B也获取ticket的值,发现同意是2。
  2. 线程A将ticket的值减 1 ,结果变为0。
  3. 但是在线程B也将进行减操作的时候,此时ticket已经变为0了,再--就变成了负数。

- -的实现中,并不是直接- -的,而是分为三步,1. 重读数据, 2.- -数据, 3.写回数据。
所以才会出现ticket为负数的情况。

进行加锁处理:

//Thread.hpp
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>

namespace ThreadMoudle
{
    class ThreadDate
    {
    public:
        ThreadDate(const std::string & name,pthread_mutex_t *lock)
        :_name(name),_lock(lock)
        {

        }
    public:
        std::string _name;
        pthread_mutex_t * _lock;
    };

    typedef void (*func_t)(ThreadDate* td); // 函数指针

    class Thread
    {
    public:
        void Excute()
        {
            std::cout << _name << " is running ..." << std::endl;
            _isrunning = true;
            _func(_td);
            _isrunning = false;
        }

    public:
        Thread(std::string name, func_t func,ThreadDate* td)
            : _name(name), _func(func),_td(td)
        {
            std::cout << "create " << name << " done ..." << std::endl;
        }

        static void *ThreadRountine(void *args)
        {
            Thread *self = static_cast<Thread *>(args);
            self->Excute();
            return nullptr;
        }
        bool Start()
        {
            int n = ::pthread_create(&_tid, nullptr, ThreadRountine, this);
            if (n != 0)
            {
                return false;
            }
            return true;
        }
        std::string Status()
        {
            if (_isrunning)
            {
                return "running...";
            }
            else
            {
                return "sleep...";
            }
        }

        void Stop()
        {
            if (_isrunning)
            {
                ::pthread_cancel(_tid);
                _isrunning = false;
                std::cout << _name << " Stop..." << std::endl;
            }
        }

        void Join()
        {
            ::pthread_join(_tid, nullptr);
            std::cout << _name << " Joined..." << std::endl;
            delete _td;
        }

        std::string Name()
        {
            return _name;
        }

        ~Thread()
        {
        }

    private:
        std::string _name;
        pthread_t _tid;
        bool _isrunning;
        func_t _func; // 回调函数
        ThreadDate* _td;
    };
}

//LockGuard.hpp
#pragma once

#include <pthread.h>

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

    ~LockGuard()
    {
        pthread_mutex_unlock(_mutex);
    }
    private:
    pthread_mutex_t * _mutex;
};
//main.cc
#include <iostream>
#include <vector>
#include <cstdio>
#include <unistd.h>
#include "Thread.hpp"
#include "LockGuard.hpp"

using namespace ThreadMoudle;

static int ticket = 10000;

// pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;

// void route(ThreadDate *td)
// {
//     std::cout << td->_name << " : " << "mutex address: " << td->_lock << std::endl;
//     sleep(1);
//     while (true)
//     {
//         ptherad_mutex_lock(td->_lock);
//         if (ticket > 0)
//         {
//             usleep(1000);
//             printf("%s get a ticket: %d \n ", name.c_str(), ticket);
//             ticket--;
//         }
//         else
//         {
//             break;
//         }
//     }
// }

void route(ThreadDate *td)
{
    while(true)
    {
        LockGuard lockguard(td->_lock);
        if(ticket > 0)
        {
            usleep(1000);
            printf("who %s, get a tickdt: %d\n",td->_name.c_str(),ticket);
            ticket--;
        }
        else
        {
            break;
        }
    }
}

static int threadnum = 4;

int main()
{
    // Thread t1("thread-1", route);
    // Thread t2("thread-2", route);
    // Thread t3("thread-3", route);
    // Thread t4("thread-4", route);

    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,nullptr);

    std::vector<Thread> threads;
    for (int i = 0; i < threadnum; i++)
    {
        std::string name = "thread" + std::to_string(i+1);
        ThreadDate* td = new ThreadDate(name,&mutex);
        threads.emplace_back(name,route,td);
    }

    for(auto &thread:threads)
    {
        thread.Start();
    }
    
    for(auto &thread:threads)
    {
        thread.Join();
    }
 
    pthread_mutex_destroy(&mutex);
    return 0;
}

以上使用了POSIX线程(pthreads)模拟了C++的多线程模块,定义了一个Thread类来封装线程的管理,以及使用一个用于自动互斥锁和解锁的LockGuard类,以确保线程的安全。
main函数中初始化一个互斥锁并且创建多个线程,这些线程执行一个共享的函数route,该函数递减共享的票务计数器,同时确保使用LockGuard的互斥。
每个线程都会打印其名称和票的编号,知道票数被抢光。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2061523.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

git commit 时发生:fatal: cannot lock HEAD ref

.git目录探析_.git文件在哪-CSDN博客https://blog.csdn.net/luofeng457/article/details/117577275 tree .git .git ├── branches ├── COMMIT_EDITMSG ├── config ├── description ├── FETCH_HEAD ├── HEAD ├── hooks │ ├── applypatch-msg.sample…

STM32的GPIO

GPIO基本控制 GPIO(General-Purpose input/output,通用输入/输出接口) 用于感知外部信号&#xff08;输入模式&#xff09;和控制外部设备&#xff08;输出模式&#xff09; 简单模块&#xff1a;LED,按键&#xff0c;蜂鸣器&#xff0c;温度传感器&#xff0c;使用一个GPIO…

qt-PLC可视化编辑器

qt-PLC可视化编辑器 一、演示效果二、核心代码三、下载链接 一、演示效果 二、核心代码 #include "diagramitem.h" #include "arrow.h"#include <QDebug> #include <QGraphicsScene> #include <QGraphicsSceneContextMenuEvent> #includ…

1 Kubeflow总体介绍-学习笔记

1 什么是 Kubeflow Kubeflow 是一个开源项目社区和生态系统&#xff0c;支持机器学习 (ML) 生命周期中的每个阶段 &#xff0c;并支持相关的开源 工具和框架&#xff0c;Kubeflow 使 Kubernetes 上的 AI/ML 变得简单、可移植且可扩展。 Kubeflow 都能提供模块化、可扩展的工具…

C语言第17篇

1.在C语言中,全局变量的存储类别是_________. A) static B) extern C) void D) register 提示&#xff1a;extern adj.外来的 register n.登记表&#xff0c;v.登记 提示与本题无关 2.在一个C源程序文件中,要定义一个只允许本源文件中所有函数使用的全局变…

JimuReport 积木报表 v1.8.0 版本发布,开源可视化报表

项目介绍 一款免费的数据可视化报表工具&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完…

【Python】成功解决 NameError: name ‘reload‘ is not defined

【Python】成功解决 NameError: name ‘reload’ is not defined 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校…

【学习笔记】7、存储器、复杂可编程器件和现场可编程门阵列

可编程逻辑器件PLD复杂可编程逻辑器件CPLD现场可编程门阵列FPGA 7.1 只读存储器&#xff08;ROM&#xff09; 7.1.1 ROM的结构 ROM存储器 存储阵列 地址译码器 输出控制电路 存储阵列&#xff0c;由许多存储单元&#xff08;1bit&#xff09;组成。每次读出一组数据&…

HTML实现俄罗斯方块

本篇文章主要讲使用HTML、CSS和JavaScript实现一个简单的俄罗斯方块游戏&#xff0c;包含基本的游戏逻辑、行消除功能以及暂停和继续游戏的控制。 使用工具 本篇文章有用到ChatGPT-4o代码纠错&#xff0c;国内免翻且稳定&#xff0c;感兴趣的大佬试试。 传送门&#xff1a;36…

认知杂谈20

今天分享 有人说的一段争议性的话 I I 程序员的高薪舒适圈&#xff1a;光鲜背后的挑战 一说起程序员这个职业&#xff0c;很多人马上就会想到高薪&#xff0c;觉得他们过着白领的生活。确实&#xff0c;程序员一般都能拿到比好多行业都高的工资&#xff0c;工作时间也比较稳…

谷粒商城实战笔记-232-商城业务-认证服务-框架效果演示-xxl-sso-徐雪里

文章目录 一&#xff0c;膜拜大神许雪里二&#xff0c;用开源框架xxl-sso演示单点登录1&#xff0c;我是老板&#xff0c;我有三个网站2&#xff0c;配置域名3&#xff0c;下载xxl-sso代码4&#xff0c;服务规划5&#xff0c;配置修改5.1 xxl-sso-server redis配置修改5.1 xxl-…

使用FModel提取黑神话悟空的资产

使用FModel提取黑神话悟空的资产 前言设置效果展示闲聊可能遇到的问题没有相应的UE引擎版本选项 前言 黑神话悟空昨天上线了&#xff0c;解个包looklook。 本文内容比较简洁&#xff0c;仅介绍解包黑神话所需的专项配置&#xff0c;关于FModel的基础使用流程&#xff0c;请见…

Python3学习(一)

目录 Python版本 标识符 保留字 注释 缩进 多行语句 同一行显示多条语句 import 与 from...import 变量 数据类型 Number&#xff08;数字&#xff09; 数字类型 数值运算 String&#xff08;字符串&#xff09; bool&#xff08;布尔类型) List&#xff08;列…

废品回收小程序,开启上门回收模式

废品回收一直是一个热门行业&#xff0c;市场发展空间巨大。随着科技的发展&#xff0c;废品回收也与时俱进&#xff0c;进行了转型&#xff01;“互联网上门回收”的新模式&#xff0c;运用信息技术的力量&#xff0c;让废品回收变得更加高效便捷&#xff0c;同时也为回收行业…

秋招红队面试经验分享

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》 《Java代码审计》 《Web安全…

【JS|第25期】探索HTTP POST请求:请求体的演变与应用

日期&#xff1a;2024年8月16日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

OpenLayers3, 缩放、平移、复位操作

文章目录 一、前言二、代码示例 一、前言 本文基于OpenLayers3实现地图缩放、平移和复位操作 二、代码示例 <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><htm…

【YOLO5 项目实战】(4)红外目标检测

欢迎关注『youcans动手学模型』系列 本专栏内容和资源同步到 GitHub/youcans 【YOLO5 项目实战】&#xff08;1&#xff09;YOLO5 环境配置与测试 【YOLO5 项目实战】&#xff08;2&#xff09;使用自己的数据集训练目标检测模型 【YOLO5 项目实战】&#xff08;3&#xff09;P…

12 程序控制语句:循环控制(while、do-while、for、多重嵌套循环、死循环)

目录 1 while 循环 1.1 基本语法 1.2 流程图 1.3 计数循环 1.3.1 实现原则 1.3.2 案例&#xff1a;循环输出语句 1.3.3 案例&#xff1a;循环输出数字 7~15 1.3.4 案例&#xff1a;倒序输出数字 56 ~ 43 1.3.5 案例&#xff1a;输出 10&#xff08;包括 10&…

SAP 预扣税配置步骤文档【Withholding Tax]

1. 配置预扣税的基本概念 预扣税是对某些支付进行扣除的税&#xff0c;可能适用于各种财务交易&#xff08;例如&#xff0c;供应商支付、股息支付等&#xff09;。预扣税通常包括几种类型&#xff0c;如个人所得税、企业所得税和其他税务种类。 2. 配置步骤 以下是一般的预…