基于抢票系统的线程互斥详解

系统--线程互斥 互斥 阅读详情

1. 封装简易线程并模拟抢票

封装简易线程

版本一:

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

namespace thread_v1
{
    static int gnumber = 1;
    using func_t = std::function<void()>;
    enum STATUS
    {
        NEW = 0, // 刚创建 还没运行
        RUNNING, // 正在运行
        STOP     // 停止
    };

    class thread
    {
    private:
        // 为什么要static?
        // pthread_create要求传一个普通的函数指针
        // 但是类内成员函数天然有一个this指针 定义在类外不优雅
        // 而静态成员函数没有this指针!
        static void *routine(void *arg)
        {
            // 参数传完了 再强转回对象原来的类型
            thread *t = static_cast<thread *>(arg);
            t->_status = STATUS::RUNNING;
            // 回调真正的业务逻辑
            t->_func();
            return nullptr;
        }
        void EnableDetach() { _joinable = false; }

    public:
        thread(func_t func)
            : _func(func), _status(STATUS::NEW), _joinable(true) // 默认是可连接
        {
            _name = "thread- " + std::to_string(gnumber++);
            _pid = getpid();
        }

        bool Start()
        {
            // 只有NEW状态下才能启动
            if (_status != STATUS::RUNNING)
            {
                int n = ::pthread_create(&_tid, nullptr, routine, this);
                if (n != 0)
                    return false;

                return true;
            }
            return false;
        }

        bool Stop()
        {
            if (_status == STATUS::RUNNING)
            {
                int n = ::pthread_cancel(_tid);
                if (n != 0)
                    return false;

                _status = STATUS::STOP;
            }
            return false;
        }

        bool Join()
        {
            // 只有处于joinable状态才能join
            if (_joinable)
            {
                int n = ::pthread_join(_tid, nullptr);
                if (n != 0)
                    return false;

                _status = STATUS::STOP;
                return true;
            }
            return false;
        }

        void Detach()
        {
            EnableDetach();
            pthread_detach(_tid);
        }

        bool IsJoinable() const { return _joinable; }
        std::string getName() const { return _name; }
        ~thread() {}

    private:
        pthread_t _tid;
        std::string _name;
        pid_t _pid;
        bool _joinable;
        func_t _func;
        STATUS _status;
    };
}

版本二:

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

namespace thread_v2
{
    static int gnumber = 1;
    enum STATUS
    {
        NEW,
        RUNNING,
        STOP
    };

    template <class T>
    class thread
    {
        using func_t = std::function<void(T)>;
    private:
        static void *routinue(void *arg)
        {
            thread<T> *t = static_cast<thread<T> *>(arg);
            t->_status = STATUS::RUNNING;
            t->_func(t->_data);
            return nullptr;
        }
        void EnableJoinable() { _joinable = false; }

    public:
        thread(func_t func, T data)
            : _func(func), _status(STATUS::NEW), _joinable(true), _pid(getpid()),_data(data)
        {
            _name = "thread_v2-" + std::to_string(gnumber++);
        }

        bool Start()
        {
            if (_status != STATUS::RUNNING)
            {
                int n = ::pthread_create(&_tid, nullptr, routinue, this);
                if (n != 0)
                    return false;

                _status = STATUS::RUNNING;
                return true;
            }
            return false;
        }

        bool Stop()
        {
            if (_joinable)
            {
                int n = ::pthread_join(_tid, nullptr);
                if (n != 0)
                    return false;

                _status = STATUS::STOP;
                _joinable = false;
                return true;
            }
            return false;
        }

        bool Join() { return Stop(); }

        void Detach()
        {
            EnableJoinable();
            pthread_detach(_tid);
        }

        std::string getName() const { return _name; }
        bool IsJoinable() const { return _joinable; }
        ~thread() {}

    private:
        pthread_t _tid;
        std::string _name;
        STATUS _status;
        func_t _func;
        pid_t _pid;
        bool _joinable;
        T _data;
    };
}

模拟抢票

利用封装的版本一模拟抢票环节:

#include "thread_v1.hpp"
using namespace std;
using namespace thread_v1;

// 模拟抢票
using namespace std;
using namespace thread_v1;

// 全局共享资源 火车票
static int tickets = 1000;

void GetTicket()
{
    while (1)
    {
        if (tickets > 0)
        {
            // 模拟抢票耗时
            usleep(1000);

            cout << "当前线程: " << pthread_self() << "抢到了第 " << tickets << " 张票\n";
            tickets--;
        }else{
            break;
        }
    }
}

int main(){
    thread t1(GetTicket);
    thread t2(GetTicket);
    thread t3(GetTicket);
    thread t4(GetTicket);

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

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

    cout << "最终剩余票数: " << tickets << endl;

    return 0;
}

img

**票数为什么会成负数!?**代码里明明有if(tickets > 0)的判断!

原子性和汇编视角解析bug

铺垫一个概念:原子性

要么全做,要么全不做->类似bool只有1/0两态—>执行时不可能被别人看到或打断

对于ticket--这个动作,在汇编层面是三条指令:

  1. LOAD -> 把内存里的tickets值读到CPU寄存器中
  2. DEC -> 在寄存器中把值减一
  3. STORE -> 把寄存器里的新值写回内存

假设现在tickets == 1

  1. 时刻1 线程A: 线程Aif判断通过,LOAD把1读到自己的寄存器
  2. 时刻2 线程切换: 线程A时间片耗尽/睡觉了usleep
    • 重点:线程A的寄存器保存的是1,线程A停在这了,内存里的tickets依然是1
  3. 时刻3 线程B: 线程B看到tickets还是1,通过if判断
  4. 时刻4 线程B: 线程B一口气跑完:读1、减1、写回0
    • 此时内存里的tickets变成0
  5. 时刻5 线程A切回: 继续执行剩下的命令:减1、写回0—>0被写了两次
    • 注意:如果是单纯的原子性问题,这里是重复更新。但如果是更加复杂的逻辑,其他线程进来,会把票数剪成负数!

总结:

tickets--不是原子操作,在CPU层面对应三条指令:LOAD DEC STORE,如果线程A在LOAD之后、STORE之前被切走,它的寄存器值就停留在那一刻,等恢复时,会用旧值计算并覆盖内存,导致其他的线程修改操作被覆盖或者在逻辑检查之后导致数据越界

2. 线程互斥—>给资源上锁解决抢票bug

在 Linux 原生线程库中,这把锁叫做 互斥量 (Mutex),全称 Mutual Exclusion。

它的工作原理非常简单:一把锁,只有一个钥匙。谁抢到钥匙谁进屋(临界区),没抢到的在门口排队(阻塞)

核心API

  1. 买锁 (初始化): pthread_mutex_init

  2. 上锁 (加锁): pthread_mutex_lock (原子操作,抢不到就睡觉)

  3. 开锁 (解锁): pthread_mutex_unlock (原子操作,唤醒排队的人)

  4. 扔锁 (销毁): pthread_mutex_destroy

代码修复—>手动上锁解锁

调用版本一:

using namespace std;

static int tickets = 1000;
// 定义一把全局锁
pthread_mutex_t mtx;

void GetTicket(){
    while(1){
        // 1. 加锁
        // 排队领钥匙,如果这时候被人拿着锁,当前线程阻塞
        pthread_mutex_lock(&mtx);

        // =========== 临界区开始 只有一个人能进入 ==========
        if(tickets > 0){
            usleep(1000);
            cout << "当前线程: " << pthread_self() << " 抢到了第 " << tickets << " 张票\n";
            tickets--;

            // 2. 解锁
            // 办完事了,赶紧还钥匙,让门口排队的人抢
            pthread_mutex_unlock(&mtx);
        } else {
            // 解锁完再走!!!!
            // 如果没票 准备break离开循环
            // 但是手里还拿着锁呢!!!直接break 这把锁永远不会释放!!!
            // 结果:门口的人等到天荒地老!死锁!
            pthread_mutex_unlock(&mtx);
            break;
        }
        // =========== 临界区结束 ===================
        // 抢完一张票后面排队去,给别人机会,不然一个线程可能解锁之后又抢到了,导致线程饥饿
        usleep(1000);
    }
}

int main(){
    // 0. 初始化锁
    // nullptr表示默认属性
    pthread_mutex_init(&mtx, nullptr);

    thread_v1::thread t1(GetTicket);
    thread_v1::thread t2(GetTicket);
    thread_v1::thread t3(GetTicket);
    thread_v1::thread t4(GetTicket);

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

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

    // 3. 销毁锁
    // 所有人都走了之后再销毁
    pthread_mutex_destroy(&mtx);

    cout << "最终剩余票数: " << tickets << endl;

    return 0;
}

封装锁并修复抢票系统

调用版本一,需要手动解锁,很容易遗忘,现在可以把锁封装成一个对象,进行自动管理

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

// 1. 封装底层的互斥锁
// 出生初始化锁 销毁扔掉锁
class Mutex{
public:
    // 禁止拷贝
    // 锁是一种独占的系统资源,拷贝会导致所有权不明,可能会二次析构或二次解锁
    Mutex(const Mutex&) = delete;
    Mutex& operator=(const Mutex&) = delete;

    Mutex(){ ::pthread_mutex_init(&_lock, nullptr); }
    void Lock() { ::pthread_mutex_lock(&_lock); }
    void Unlock() { ::pthread_mutex_unlock(&_lock); }
    ~Mutex() { ::pthread_mutex_destroy(&_lock); }

private:
    pthread_mutex_t _lock;
};

// 2. 封装锁的守卫 RAII
// 创建自动上锁 销毁自动解锁
class LockGuard{
private:
    Mutex& _mutex;  // 要操作锁 而不是操作拷贝的锁
public:
    LockGuard(Mutex& mutex) :_mutex(mutex) { _mutex.Lock(); }
    ~LockGuard() { _mutex.Unlock(); }
};

修复抢票系统:

// RAII加锁
#include "LockGuard.hpp"

using namespace std;

// 1. 定义共享数据结构
// 锁和资源绑定
struct TicketData{
    int tickets;
    Mutex mtx;
    string name;
    
    TicketData(int t, string n)
        :tickets(t)
        ,name(n)
        {}
};

// 2. 抢票逻辑
void GetTicket(TicketData* data){
    while(1){
        // 作用域块:一个局部遍历区域,用{}把临界区包起来
        {
            // 1. 构造guard对象 自动调用mtx.Lock()
            LockGuard guard(data->mtx);

            if(data->tickets > 0){
                usleep(1000);
                cout << "线程 " << pthread_self() << " (" << data->name
                     << ") 抢到了第 " << data->tickets << " 张票\n";
                data->tickets--;
            }else{
                // guard对象离开作用域,析构自动调用mtx.Unlock()
                break;
            }
        }
        usleep(1000);
    }
    cout << "线程 " << pthread_self() << " 退出抢票...\n";
}

int main(){
    // 准备共享数据
    // 数据必须在堆上或者主线程栈上,保证声明周期比子线程更长
    TicketData* global_data = new TicketData(1000,"抢票系统");

    // T被推到为TicketData*
    thread_v2::thread<TicketData*> t1(GetTicket, global_data);
    thread_v2::thread<TicketData*> t2(GetTicket, global_data);
    thread_v2::thread<TicketData*> t3(GetTicket, global_data);
    thread_v2::thread<TicketData*> t4(GetTicket, global_data);

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

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

    cout << "最终剩余票数: " << global_data->tickets << endl;

    delete global_data;
    return 0;

}

锁的原理

锁的本质:原子指令

在汇编层面,普通的int a = 0或者if(a == 0)都不是原子的。CPU提供了特殊的原子指令,比如 Test-And-Set (TAS) 或者 Compare-And-Swap (CAS),以及 x86 下的 xchg(交换)指令

伪汇编代码演示(以 xchg 锁为例): 假设内存中有一个变量 mutex,0代表空闲,1代表被锁:

lock:
    mov  al, 1      ; 把寄存器 al 设为 1
    xchg al, mutex  ; 【关键一步】原子交换寄存器al和内存mutex的值
    ; 这一步由硬件保证,哪怕有100个CPU核同时执行,
    ; 总线仲裁器也只允许一个先执行,绝对不会同时发生。
    
    test al, al     ; 检查 al 里的旧值
    jnz  suspend    ; 如果 al 原来就是 1(说明别人锁着呢),我就挂起(去排队)
    ret             ; 如果 al 原来是 0,现在内存里变成 1 了,且我拿到了 0,加锁成功!

unlock:
    mov  mutex, 0   ; 简单的把内存改回 0
    ret             ; 唤醒排队的人

互斥锁的底层依赖于CPU提供的原子指令(如x86的xchg),保证了读取-修改-回写这一连串操作在硬件层面是不会被打断的

image-20260126221600885

光学相干断层扫描(OCT)在 妇科诊断 中的革命性应用 尽管OCT在妇科的应用提供了许多潜在的优势,但它仍然是一个相对新的技术,并不是所有医疗机构都有能力提供这种类型的成像服务。随着技术的发展和医生对OCT的熟悉程度的提高,预计OCT将在妇科和其他医学领域中扮演越来越重要的角色。早期发现和准确的分类是提高治疗成功率的关键。虽然这是一个相对较新的应用领域,但OCT的非侵入性和高分辨率成像能力可能提供有价值的信息,以提高辅助生殖技术的成功率。:光学相干断层扫描(OCT)在阴道和外阴组织的评估中的应用正在逐渐展开,提供了一种新的方法来诊断和监测各种妇科疾病。 阅读详情

相关推荐

Github多人协作

最近有门课程需要几个人一起开发一个项目,此前我们基本都是单干的,多人合作意味着需要及时将我们每个人的代码进行合并,U盘传文件这种方式太慢了,效率不行,而且没法解决两个人同时修改一个文件的问题,这次我们就尝试使用Github来协助开发。下面就来记录一下这次开发遇到的一些问题和解决的方法。 Git的安装 从官网下载最新版按照默认安装即可。 此处不多加说明。 Git常用命令 在此放上一...

yzr1216的博客 1万+

线程池-抢票系统性能优化

Java线程池是一种用于优化线程使用和管理的工具,它通过复用一定数量的线程来执行多个任务,从而减少了创建和销毁线程的开销,提高了程序的性能和响应速度。在前面,我们通过创建多个线程模拟抢票场景,并且使用加锁的方式解决了车票超卖的问题。‼️当用户量较大时,就需要频繁的创建线程对象、释放线程对象,十分麻烦。池化后,性能得到了质的飞跃🚀。Java中的线程池是通过。接口及其子类来实现的。

记录大学四年代码 888

Oracle数据库安装及配置(Oracle19c)

Windows 10安装Oracle 19c教程

weixin_69200200的博客 2万+

【Linux】线程同步与互斥

介绍线程的互斥与同步,锁的概念

dab112的博客 606

DAIC-WOZ抑郁数据集实战:从申请到特征提取的全流程避坑指南

本文详细解析了DAIC-WOZ抑郁数据集从申请到特征提取的全流程,提供了实用的避坑指南和代码模板。涵盖数据获取技巧、多模态特征工程(面部动作单元、音频特征、文本处理)以及样本不平衡解决方案,帮助研究者高效处理这一经典抑郁分析数据集。

weixin_29196315的博客 172

PID控制参数整定(调节方法)原理+图示+MATLAB调试

序 首先最重要的是了解每个参数调节了系统响应的那些属性,通过观察响应从而调节参数改变属性。 PID的作用概述: 1、P产生响应速度和力度,过小响应慢,过大会产生振荡,是I和D的基础。 2、I在有系统误差和外力作用时消除偏差、提高精度,同时也会增加响应速度,产生过冲,过大会产生振荡。 3、D抑制过冲和振荡,过小系统会过冲,过大会减慢响应速度。D的另外一个作用是抵抗外界的突发干扰,阻止系统的突变。 同时调节的顺序是:P>I>D 下面了解的一个很重要的就是调节的目标,也就是最好的响应曲线.

viafcccy的博客 16万+

【Linux】<互斥量>解决<抢票问题>——【多线程竞争问题】

一.抢票问题展示——"票数变成负数"1.问题展示:2.“ticket--”执行的过程是''非原子的'':多个线程会进入该代码段二.互斥&临界区&临界资源三.互斥量(锁)1.互斥量的初始化(动态&静态)2.互斥量的销毁3.互斥量的加锁&解锁四.解决

YYDsis的博客 1444

【maven】idea左侧External Libraries里,没有Maven的依赖包 代码飘红

文章目录1.概述M.参考 1.概述 因为内网开发,然后这几天我被隔离了,然后公司搞了一个外网的vpn能连接的云桌面,云桌面也是网络隔离的,因此只能自己拷贝相关的文件到里面。 然后我就拷贝了代码和maven仓库中的一部分,因为本地的20G一次拷贝不完,需要多次拷贝,但是我拷贝了kafka,仍然飘红。具体如下 idea左侧External Libraries里,没有Maven的依赖包 然后整了很久都没搞定,后来我自己新建了一个项目,然后引入发现是不可以的,而且框框里面的飘红。 然后我就想是不是因为缺少依赖

九师兄 4412

Linux之线程同步与互斥,模拟抢票系统

线程的同步与互斥 相关背景概念 临界区 :每个线程内部访问自己资源的代码叫做临界区: 临界资源 :多个执行流共享的资源叫做临界资源 互斥 :在任何时刻,只能有且仅有一个执行流访问临界区的临界资源 原子性 :一个操作不会被任何调度机制打断,要么完成,要么没完成,只有两种状态,常见的++,–都不是原子操作,因为汇编代码不止一条 互斥量mutex 大部分情况,线程使用的数据都是局部变量,变量的地址...

qq_39412582的博客 977

Python爬虫实战:逆向工程与异步处理攻克Pinterest无限滚动瀑布流

我们将从零开始,逐步分析其数据接口,构建一个功能完整的爬虫,并深入探讨反爬虫策略的应对、性能优化以及数据存储等高级主题。: 在实际爬取中,你需要先访问一次Pinterest主页,获取初始的Cookie和CSRF Token,然后再发起API请求。: Python爬虫,Pinterest,无限滚动,瀑布流,异步IO,httpx,逆向工程,API接口,动态内容,反爬虫。你会很快发现一些关键的请求模式。:页面初始加载有限内容,随着用户滚动,新的图片块通过AJAX动态加载,实现“永无止境”的浏览。

2201_76125261的博客 552

揭秘AI人工智能中Stable Diffusion的模型微调技巧

Stable Diffusion作为一种强大的文本到图像生成模型,在艺术创作、设计、娱乐等多个领域展现出了巨大的应用潜力。然而,其默认的模型输出可能无法满足特定用户或特定场景的个性化需求。本文章的目的在于深入揭秘Stable Diffusion的模型微调技巧,让读者能够根据自己的需求对模型进行定制化调整,生成符合特定风格、主题或要求的图像。范围涵盖了从基础概念到实际操作,从理论原理到项目实战的全方位内容,旨在为读者提供一个系统且深入的学习指南。

大模型应用工坊 950

Linux程序开发(十二):线程与多线程同步互斥实现抢票系统

某公司的演唱会门票正在热销中,为了让用户更好地体验购票过程,公司决定开启一个抢票系统。具体来说,用户可以在系统中选择想要购买的门票数量,系统会随机生成几个购票请求,并给出这些请求的优先级(优先级越高,越先处理)。每个请求需要购买一定数量的门票,如果门票数量已经不足,则该请求将会被拒绝。如果多个请求同时到达,需要按照优先级进行处理。在处理完一个请求后,系统需要输出当前剩余的门票数量。 请你使用Linux下的线程和多线程同步互斥知识,编写一个C程序实现这个抢票系统,要求满足以下条件: 1、系统需要启动两个线程

m0_60732994的博客 1322

高通开发系列 - Camera之摄像头调试记录

记录一些调试内容

fulinux的博客 1217

[Linux] 黄牛抢票系统(互斥锁实现互斥)

version 1.0 /************************************************************************* *> version 1.0 *> File Name: lock.c *> Author: giturtle *> Describe: 实现互斥锁的基本使用以及线程安全的基本认识 黄牛抢票--...

giturtle's blog 1150

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

[操作系统] 线程互斥

互斥量的实现原理。

kevin_blog 1904

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

多线程抢票_java多线程下模拟抢票

我们设置三个对象分别同时抢20张票,利用多线程实现.public class Web123506 implements Runnable{private int ticteksNums=20;//票数@Overridepublic void run() {while (true){if(ticteksNums<0){break;}/* try {//睡眠Thread.sleep(200);}...

weixin_39672160的博客 1020

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

【Linux】多线程安全之道:互斥、加锁技术与底层原理

本文主要讲解了线程的互斥的实现和底层理解

可涵的博客 2004

Re:Linux系统篇(五十三)线程篇 · 六:从“抢票超卖”到“并发安全”:一文搞懂 Linux 线程互斥与互斥锁(Mutex)

本文介绍了Linux多线程编程中的互斥量(mutex)概念与应用。作者通过模拟抢票程序演示了多线程并发操作共享变量时可能出现的负数票数问题,分析了问题根源在于非原子性操作导致的数据竞争。文章详细解释了互斥量的工作原理,包括互斥量的初始化方式(静态分配PTHREAD_MUTEX_INITIALIZER和动态pthread_mutex_init)、销毁注意事项,并强调互斥锁的使用原则(尽量缩小加锁范围)。最后指出互斥量是解决线程同步问题的关键机制,下一篇文章将深入讲解互斥量的实现原理和封装方法。

小此方的博客 4371

【Linux系统编程】第四十五弹---线程互斥:从问题到解决,深入探索互斥量的原理与实现

线程互斥:见一见多线程访问问题,解决多线程访问问题:互斥量的接口,互斥量接口的使用,原理角度理解锁,实现角度理解~~~

小林熬夜学习编程的博客 1418

Linux多线程——线程互斥与同步

摘要:多线程并发访问共享资源时存在数据竞争问题,需通过互斥锁和同步机制解决。 临界资源与竞争问题:多线程同时操作全局变量(如g_val--)时,由于CPU指令的非原子性,可能导致数据不一致。例如,线程切换时未完成的操作会使变量值异常。 互斥锁保护:通过pthread_mutex_t实现加锁(pthread_mutex_lock)和解锁(pthread_mutex_unlock),确保临界区代码的原子性。锁的粒度应尽量细以减少性能损失。 线程同步:条件变量(pthread_cond_t)解决线程间执行顺序问题

2401_83603768的博客 1455

【Linux系统化学习】线程互斥 | 互斥量(锁)

本片文章介绍了Linux下的线程互斥问题!!!

qq_55119554的博客 1312

Linux多线程【线程互斥与同步】

初学者在使用 多线程 并发执行任务时一定会遇到 并发访问的问题,最直观的感受就是每次运行得出的结果值大概率不一致,这种执行结果不一致的现象是非常致命,因为它具有随机性,即结果可能是对的,也可能是错的,无法可靠的完成任务,类似物理学神兽 薛定谔的猫

北 海的博客 3042

【linux系统编程】线程互斥

本篇文章着重讲的是线程互斥的内容,我们知道线程创建处理几乎大部分资源都是共享的,那多线程访问同一份共享资源有没有什么问题,本文从这里开始引入,并开始介绍互斥锁,使用互斥锁的函数,并对互斥锁进行了简单的封装, 有了锁会不会产生问题?由此引出死锁的问题,以及解决方法!

fight_p的博客 1552

【Linux操作系统】多线程抢票逻辑——学习互斥量(锁)函数接口

Linux操作系统——多线程抢票逻辑,锁(互斥量)

weixin_63449996的博客 864

LINUX系统编程:多线程互斥

这也就解释了,为什票会抢到负数,究其原因就是我们抢票+判断的操作不是原子的,所以我们要通过互斥锁把这两个操作编程"原子"的,这个原子是在线程看来是原子的,不是真正意义上的原子。thread1开始执行,thread1进行判断,thread1发现票数也是大于0的,进入循环,这个时候hread1的时间片到了,thread1进入等待队列。thread0进行判断,thread0发现票数是大于0的,他就会进入循环,但是这个时候thread0的时间片到了,thread0进入等待队列。3.销毁的互斥量,就不要在加锁了。

W2155的博客 924

Python多线程实战:12306抢票系统的并发处理优化

12306抢票系统通过多线程技术显著提升性能,主要应用于CDN筛选和用户状态检查两大场景。CDN筛选采用守护线程并行测试节点响应速度,快速筛选出优质节点;用户状态检查线程后台运行,定期验证登录有效性。系统巧妙避免线程安全问题,采用守护线程设计确保主线程退出时自动终止。多线程使CDN筛选时间从10分钟缩短至分钟级,同时保持登录状态有效,大幅提高抢票成功率。但也需注意CPU消耗、网络带宽占用等问题,体现了Python多线程在I/O密集型任务中的高效应用。

qq_21106275的博客 1259
上一篇: 线程概念和线程控制
下一篇: 基于生产者-消费者模型下的线程同步综述
Vect__
博客等级 码龄1年 204粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值