操作系统底层运行原理 —— 基于线程安全的消息机制

news2024/11/22 23:10:42

前言

学过Android应用开发的大概都知道Handler这个东东,这也是面试中老生常谈的问题。其实不仅仅是Android,iOS以及PC的操作系统,底层也离不开消息机制。这个属于生产消费者问题。

什么是生产者消费者模式

生产者消费者模式(Producer-Consumer Pattern)是一种多线程协作的设计模式,其中有两种不同类型的线程:生产者和消费者。这两种线程通过共享的缓冲区(队列、缓冲池等)进行通信,以协调任务的执行。

在生产者消费者模式中,生产者负责生成一些数据或执行一些任务,并将其放置到共享的缓冲区中。而消费者则负责从缓冲区中获取数据或任务,并进行处理。这样,生产者和消费者可以在不同的时间段内独立地工作,通过共享的缓冲区进行数据交换,实现了解耦和协作。

典型的生产者消费者模式中包含以下元素:

  1. 生产者(Producer): 生成数据或执行任务,并将其放入共享缓冲区。
  2. 消费者(Consumer): 从共享缓冲区获取数据或任务,并进行相应的处理。
  3. 共享缓冲区: 用于存储生产者生成的数据或任务,以供消费者获取。
  4. 同步机制: 用于确保生产者和消费者之间的协调和同步,防止竞争条件(Race Condition)和死锁(Deadlock)等问题。

生产者消费者模式的一个典型场景是任务队列。生产者将任务放入队列,而消费者从队列中取出任务并执行。这种模式可以有效地利用多线程的优势,提高系统的效率和性能。

使用C++手写一个简易的消息处理机制

效果演示

20231223_005254.gif

完整代码
//
//  main.cpp
//  ProducerCustomer
//
//  Created by dora on 2023/12/23.
//

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

#define EVENT_TYPE_1 0
#define EVENT_TYPE_2 1
#define EVENT_TYPE_3 2

std::queue<int> buffer;             // 共享缓冲区
std::mutex mtx;                     // 互斥锁
std::condition_variable condition;         // 条件变量
const int bufferSize = 5;            // 消息的缓冲区大小

// 生产者线程函数
void producer() {
    while (true) {
        {
            // 生产者休眠40毫秒,太快会导致消费者线程处理不过来,如造成UI卡顿掉帧
            std::chrono::milliseconds sleepDuration(40);
            std::this_thread::sleep_for(sleepDuration);
            std::unique_lock<std::mutex> lock(mtx);
            // 等待条件满足:缓冲区不满
            condition.wait(lock, [] { return buffer.size() < bufferSize; });
            // 生产数据并放入缓冲区
            buffer.push(EVENT_TYPE_1);
            std::cout << "Produced: " << EVENT_TYPE_1 << " Event Size:("<< buffer.size() <<")\n";
        }
        // 通知消费者缓冲区非空
        condition.notify_all();
    }
}

void producer2() {
    while (true) {
        {
            // 生产者休眠40毫秒,太快会导致消费者线程处理不过来,如造成UI卡顿掉帧
            std::chrono::milliseconds sleepDuration(40);
            std::this_thread::sleep_for(sleepDuration);
            std::unique_lock<std::mutex> lock(mtx);
            // 等待条件满足:缓冲区不满
            condition.wait(lock, [] { return buffer.size() < bufferSize; });
            // 生产数据并放入缓冲区
            buffer.push(EVENT_TYPE_2);
            std::cout << "Produced: " << EVENT_TYPE_2 << " Event Size:("<< buffer.size() <<")\n";
        }
        // 通知消费者缓冲区非空
        condition.notify_all();
    }
}

void producer3() {
    while (true) {
        {
            // 生产者休眠40毫秒,太快会导致消费者线程处理不过来,如造成UI卡顿掉帧
            std::chrono::milliseconds sleepDuration(40);
            std::this_thread::sleep_for(sleepDuration);
            std::unique_lock<std::mutex> lock(mtx);
            // 等待条件满足:缓冲区不满
            condition.wait(lock, [] { return buffer.size() < bufferSize; });
            // 生产数据并放入缓冲区
            buffer.push(EVENT_TYPE_3);
            std::cout << "Produced: " << EVENT_TYPE_3 << " Event Size:("<< buffer.size() <<")\n";
        }
        // 通知消费者缓冲区非空
        condition.notify_all();
    }
}

// 消费者线程函数
void consumer() {
    while (true) {
        {
            std::unique_lock<std::mutex> lock(mtx);
            // 等待条件满足:缓冲区非空
            condition.wait(lock, [] { return !buffer.empty(); });
            // 消费数据
            int data = buffer.front();
            buffer.pop();
            std::cout << "Consumed: " << data << " Event Size:("<< buffer.size() <<")\n";
        }
        // 通知生产者缓冲区不满
        condition.notify_all();
    }
}

int main() {
    // 创建生产者和消费者线程
    std::thread producerThread(producer);
    std::thread producerThread2(producer2);
    std::thread producerThread3(producer3);
    std::thread consumerThread(consumer);

    // 等待线程结束
    producerThread.join();
    producerThread2.join();
    producerThread3.join();
    consumerThread.join();
    system("pause");
    return 0;
}
代码解析

这里一共使用了三个生产者线程,一个消费者线程。消费者线程通常就是我们Android中的UI线程或者叫主线程,Handler mH所对应的就是主线程,因为它对应的就是主线程的Looper。由于这只是一个简单的模拟,实际上还会使用到如thread_local_posix.h,Linux使用的是POSIX标准的线程。生产者模拟的就是消息的发送者,消费者模拟的就是消息处理器Handler,所有消息发送到主线程的Handler统一处理,比如发送广播、启动服务、打开一个Activity等。

mutex: 互斥变量

unique_lock: 锁,作用同 java.util.concurrent.locks.Lock

condition_variable: 条件变量,作用同 java.util.concurrent.Condition

当然Java中我们通常使用的是ReentrantLock。std::unique_lock的析构函数会自动释放锁,从而避免了手动调用 unlock。在性能上,ReentrantLock支持可重入性,需要维护更多的状态信息,所以无论是算法复杂度还是语言上性能都是不如std::unique_lock的。queue的front函数只查看队列最前面的元素,而不移除掉,而pop函数则是移除掉队列最前面的元素。[] {}是一个Lambda表达式,C++11开始支持的特性。condition_variable的notify_allnotify_one都是用于通知其他等待锁被释放的线程可以开始执行,只不过notify_all会通知所有等待在条件变量上的线程,让它们都尝试重新获取锁并继续执行。这样,所有等待的线程都有机会获得锁。而notify_one只会通知等待在条件变量上的一个线程,具体是哪一个线程取决于实现和调度器的策略。这样,只有一个线程会获得锁,其他线程仍然保持等待状态。

总结

线程安全是多线程开发的一个必不可少的技能,让多线程可以更好的协作执行任务。相当于在马路上制定了交通规则,即使很多车,也不会相撞。当然在现实生活中,会有意外发生,在程序的世界里,除非程序异常退出,否则都是按规则执行任务的。本文主要是让大家熟悉一下C/C++底层语言,为Android NDK开发打基础。

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

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

相关文章

【UE Websocket】“WebSocket Server”插件使用记录

1. 在商城中下载“WebSocket Server”插件 该插件具有如下节点&#xff0c;基本可以满足WebSocket服务端的所有需求 2. 如果想创建一个基本的服务端&#xff0c;我们可以新建一个actor蓝图&#xff0c;添加如下节点 3. UE运行后&#xff0c;我们可以使用在线的websocket测试助手…

使用MicroPython和pyboard开发板(15):使用LCD和触摸传感器

使用LCD和触摸传感器 pybaord的pyb对LCD设备也进行了封装&#xff0c;可以使用官方的LCD显示屏。将LCD屏连接到开发板&#xff0c;连接后。 使用LCD 先用REPL来做个实验&#xff0c;在MicroPython提示符中输入以下指令。请确保LCD面板连接到pyboard的方式正确。 >>…

认识NXP新型微处理器:MCX工业和物联网微控制器

目录 概述 1 MCX工业和物联网微控制器介绍 2 MCX 系列微控制器类型 2.1 MCX N系列微控制器 2.1.1 主要特征 2.1.2 MCX N系列产品 2.1.3 MCX N9xx和N5xx MCU选型表 2.2 MCX A系列微控制器 2.2.1 主要特征 2.2.2 MCX A系列产品 2.2.3 MCX A MCU的架构 2.3 MCX W系…

Unity射击游戏开发教程:(24)创造不同的敌人

在这篇文章中,我们将讨论添加一个可以承受多次攻击的新敌人和一些动画来使事情变得栩栩如生。敌人没有任何移动或射击行为。这将有助于增强未来敌人的力量。 我们将声明一个 int 来存储敌人可以承受的攻击数量,并将其设置为 3。

Unity修改Project下的Assets的子文件的图标

Unity修改文件夹的图标 示例&#xff1a; 在右键可以创建指定文件夹。 github链接 https://github.com/SeaeeesSan/SimpleFolderIconCSDN资源的链接 https://download.csdn.net/download/GoodCooking/89347361 去GitHub下载支持原作者哦。重要的事情 截图来自GitHub 。 U…

信息系统项目管理师0127:工具与技术(8项目整合管理—8.6管理项目知识—8.6.2工具与技术)

点击查看专栏目录 文章目录 8.6.2 工具与技术8.6.2 工具与技术 专家判断管理项目知识过程中,应征求具备如下领域相关专业知识或接受过相关培训的个人或小组的意见,涉及的领域包括:知识管理、信息管理、组织学习、知识和信息管理工具以及来自其他项目的相关信息等。 知识管理…

【2024】高校网络安全管理运维赛

比赛时间&#xff1a;2024-05-06 Re-easyre 基本的base64换表&#xff0c;用CyberChef解密 Re-babyre 进入主函数&#xff0c;发现输入四次 看一下就知道是大数求解 (当初写的时候差不多 不知道为什么第四个总是算错…) from z3 import *s Solver() # 设置一个解方程的类…

产品经理-需求收集(二)

1. 什么是需求 指在一定的时期中&#xff0c;一定场景中&#xff0c;无论是心理上还是生理上的&#xff0c;用户有着某种“需要”&#xff0c;这种“需要”用户自己不一定知道的&#xff0c;有了这种“需要”后用户就有做某件事情的动机并促使达到其某种目的&#xff0c;这也就…

Redis 主从复制、哨兵与集群

一、Redis 主从复制 1. 主从复制的介绍 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。 默认情况下&a…

如何快速从手动测试转向自动化测试

寻求具有无缝持续集成和持续交付 (CI/CD) 的高效 DevOps 管道比以往任何时候都更加重要。想象一下这样一个场景&#xff1a;您的软件组织显著减少了人工工作量、降低了成本&#xff0c;并更加自信地发布了软件更新。换句话说&#xff0c;通过将 Web UI 和 API 测试结合在一起&a…

展现金融科技前沿力量,ATFX于哥伦比亚金融博览会绽放光彩

不到半个月的时间里&#xff0c;高光时刻再度降临ATFX。而这一次&#xff0c;是ATFX不曾拥有的桂冠—“全球最佳在线经纪商”(Best Global Online Broker)。2024年5月15日至16日&#xff0c;拉丁美洲首屈一指的金融盛会—2024年哥伦比亚金融博览会(Money Expo Colombia 2024) 于…

前端开发攻略---用Vue实现无限滚动的几种方法

目录 1、原理 2、使用CSS动画 代码&#xff1a; 3、使用JS实现 代码&#xff1a; 1、原理 复制内容&#xff1a;将需要滚动的内容复制一次&#xff0c;并将这些副本放置在原始内容的后面。这样&#xff0c;当用户滚动到内容的末尾时&#xff0c;就会无缝地切换回到内容的起…

【Python】—— 公共的方法

目录 &#xff08;一&#xff09;公共操作 1.1 公共操作之运算符加号 1.2 公共操作之运算符乘号 1.3 公共操作之运算符判断数据是否存在 &#xff08;二&#xff09;公共方法 2.1 公共方法-len 2.2 公共方法-del 2.3 公共方法-max和min 2.4 公共方法-range 2.5 公共方…

如果 SEC 批准以太坊现货 ETF,会有更多山寨币 ETF 吗?

撰文&#xff1a;Protos 编译&#xff1a;Ismay&#xff0c;BlockBeats 文章来源香港Web3媒体Techun News 编者按&#xff1a;SEC 已与交易平台和 ETF 申请人就 19b-4 规则变更请求进行沟通&#xff0c;这表明以太坊现货 ETF 获批的可能性大大增加。与此同时山寨币投资者猜测…

嵌入式开发中树莓派和单片机关键区别

综合了几篇帖子作以信息收录&#xff1a;树莓派和单片机作为嵌入式系统领域中两种广泛使用的设备&#xff0c;各自有着不同的特性和应用场景&#xff0c;文章从五个方面进行比对展开。 架构与性能&#xff1a; 树莓派&#xff1a;是一款微型计算机&#xff0c;通常配备基于AR…

解决 git:OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 0

解决 git&#xff1a;OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 0 问题 git pull报错&#xff1a;fatal: unable to access ‘https://github.com/aircrushin/ultrav-music.git/’: Failed to connect to github.com port 443 after 21077 ms: Couldn’t connect to serve…

成本管控:如何利用 SOLIDWORKS Costing 高效估算成本?

现在全球材料短缺、生活成本上升以及能源价格上涨而导致的成本上升问题突显。 生产产品需要的成本以及如何让产品的成本下降就成为很多的企业越来越关注的问题。 SOLIDWORKS Costing是集成到 SOLIDWORKS Professional 和 Premium 中的一款允许用户和制造商估算产品生产成本的工…

Java 多线程抢红包

问题需求 一个人在群里发了1个100元的红包&#xff0c;被分成了8个&#xff0c;群里有10个人一起来抢红包&#xff0c;有抢到的金额随机分配。 红包功能需要满足哪些具体规则呢? 1、被分的人数抢到的金额之和要等于红包金额&#xff0c;不能多也不能少。 2、每个人至少抢到1元…

A股翻车现场

英伟达业绩炸裂&#xff0c;但今天A股这边不仅没喝着汤&#xff0c;还再度上演大型翻车现场&#xff0c;人家不仅股价大涨7个点还站上1000美元大关&#xff0c; 而咱A股里的英伟达&#xff0c;AI&#xff0c;TMT相关概念股&#xff0c;包括工业&#xff08;富联&#xff09;&am…

Redis常见数据类型(4) - hash, List

hash 命令小结 命令执行效果时间复杂度hset key field value设置值O(1)hget key field获取值O(1)hdel key field [field...]删除值O(k), k是field个数hlen key计算field个数O(1)hgetall key获取所有的field-valueO(k), k是field的个数hmget field [field...]批量获取field-va…