当前位置: 首页 > news >正文

鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据

基本概念

  • 队列又称消息队列,是一种常用于任务间通信的数据结构。队列接收来自任务或中断的不固定长度消息,并根据不同的接口确定传递的消息是否存放在队列空间中。

  • 任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。如果将读队列和写队列的超时时间设置为0,则不会挂起任务,接口会直接返回,这就是非阻塞模式。

  • 消息队列提供了异步处理机制,允许将一个消息放入队列,但不立即处理。同时队列还有缓冲消息的作用。

  • 队列用于任务间通信,可以实现消息的异步处理。同时消息的发送方和接收方不需要彼此联系,两者间是解耦的。

队列特性

  • 消息以先进先出的方式排队,支持异步读写。
  • 读队列和写队列都支持超时机制。
  • 每读取一条消息,就会将该消息节点设置为空闲。
  • 发送消息类型由通信双方约定,可以允许不同长度(不超过队列的消息节点大小)的消息。
  • 一个任务能够从任意一个消息队列接收和发送消息。
  • 多个任务能够从同一个消息队列接收和发送消息。
  • 创建队列时所需的队列空间,默认支持接口内系统自行动态申请内存的方式,同时也支持将用户分配的队列空间作为接口入参传入的方式。

消息队列长什么样?

#ifndef LOSCFG_BASE_IPC_QUEUE_LIMIT
#define LOSCFG_BASE_IPC_QUEUE_LIMIT 1024 //队列个数
#endif
LITE_OS_SEC_BSS LosQueueCB *g_allQueue = NULL;//消息队列池
LITE_OS_SEC_BSS STATIC LOS_DL_LIST g_freeQueueList;//空闲队列链表,管分配的,需要队列从这里申请typedef struct {UINT8 *queueHandle; /**< Pointer to a queue handle */	//指向队列句柄的指针UINT16 queueState; /**< Queue state */	//队列状态UINT16 queueLen; /**< Queue length */	//队列中消息总数的上限值,由创建时确定,不再改变UINT16 queueSize; /**< Node size */		//消息节点大小,由创建时确定,不再改变,即定义了每个消息长度的上限.UINT32 queueID; /**< queueID */			//队列IDUINT16 queueHead; /**< Node head */		//消息头节点位置(数组下标)UINT16 queueTail; /**< Node tail */		//消息尾节点位置(数组下标)UINT16 readWriteableCnt[OS_QUEUE_N_RW]; /**< Count of readable or writable resources, 0:readable, 1:writable *///队列中可写或可读消息数,0表示可读,1表示可写LOS_DL_LIST readWriteList[OS_QUEUE_N_RW]; /**< the linked list to be read or written, 0:readlist, 1:writelist *///挂的都是等待读/写消息的任务链表,0表示读消息的链表,1表示写消息的任务链表LOS_DL_LIST memList; /**< Pointer to the memory linked list */	//@note_why 这里尚未搞明白是啥意思 ,是共享内存吗?
} LosQueueCB;//读写队列分离

解读

  • 和进程,线程,定时器一样,消息队列也由全局统一的消息队列池管理,池有多大?默认是1024
  • 鸿蒙内核对消息的总个数有限制,queueLen消息总数的上限,在创建队列的时候需指定,不能更改.
  • 对每个消息的长度也有限制, queueSize 规定了消息的大小,也是在创建的时候指定.
  • 为啥要指定总个数和每个的总大小,是因为内核一次性会把队列的总内存(queueLen*queueSize)申请下来,确保不会出现后续使用过程中内存不够的问题出现,但同时也带来了内存的浪费,因为很可能大部分时间队列并没有跑满.
  • 队列的读取由queueHeadqueueTail管理,Head表示队列中被占用的消息节点的起始位置。Tail表示被占用的消息节点的结束位置,也是空闲消息节点的起始位置。队列刚创建时,Head和Tail均指向队列起始位置
  • 写队列时,根据readWriteableCnt[1]判断队列是否可以写入,不能对已满(readWriteableCnt[1]为0)队列进行写操作。写队列支持两种写入方式:向队列尾节点写入,也可以向队列头节点写入。尾节点写入时,根据Tail找到起始空闲消息节点作为数据写入对象,如果Tail已经指向队列尾部则采用回卷方式。头节点写入时,将Head的前一个节点作为数据写入对象,如果Head指向队列起始位置则采用回卷方式。
  • 读队列时,根据readWriteableCnt[0]判断队列是否有消息需要读取,对全部空闲(readWriteableCnt[0]为0)队列进行读操作会引起任务挂起。如果队列可以读取消息,则根据Head找到最先写入队列的消息节点进行读取。如果Head已经指向队列尾部则采用回卷方式。
  • 删除队列时,根据队列ID找到对应队列,把队列状态置为未使用,把队列控制块置为初始状态。如果是通过系统动态申请内存方式创建的队列,还会释放队列所占内存。
  • 留意readWriteList,这又是两个双向链表, 双向链表是内核最重要的结构体,牢牢的寄生在宿主结构体上.readWriteList上挂的是未来读/写消息队列的任务列表.

初始化队列

LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit(VOID)//消息队列模块初始化
{LosQueueCB *queueNode = NULL;UINT32 index;UINT32 size;size = LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB);//支持1024个IPC队列/* system resident memory, don't free */g_allQueue = (LosQueueCB *)LOS_MemAlloc(m_aucSysMem0, size);//常驻内存if (g_allQueue == NULL) {return LOS_ERRNO_QUEUE_NO_MEMORY;}(VOID)memset_s(g_allQueue, size, 0, size);//清0LOS_ListInit(&g_freeQueueList);//初始化空闲链表for (index = 0; index < LOSCFG_BASE_IPC_QUEUE_LIMIT; index++) {//循环初始化每个消息队列queueNode = ((LosQueueCB *)g_allQueue) + index;//一个一个来queueNode->queueID = index;//这可是 队列的身份证LOS_ListTailInsert(&g_freeQueueList, &queueNode->readWriteList[OS_QUEUE_WRITE]);//通过写节点挂到空闲队列链表上}//这里要注意是用 readWriteList 挂到 g_freeQueueList链上的,所以要通过 GET_QUEUE_LIST 来找到 LosQueueCBif (OsQueueDbgInitHook() != LOS_OK) {//调试队列使用的.return LOS_ERRNO_QUEUE_NO_MEMORY;}return LOS_OK;
}

解读

  • 初始队列模块,对几个全局变量赋值,创建消息队列池,所有池都是常驻内存,关于池后续有专门的文章整理,到目前为止已经解除到了进程池,任务池,定时器池,队列池,==
  • LOSCFG_BASE_IPC_QUEUE_LIMIT个队列挂到空闲链表g_freeQueueList上,供后续分配和回收.熟悉内核全局资源管理的对这种方式应该不会再陌生.

创建队列

//创建一个队列,根据用户传入队列长度和消息节点大小来开辟相应的内存空间以供该队列使用,参数queueID带走队列ID
LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate(CHAR *queueName, UINT16 len, UINT32 *queueID,UINT32 flags, UINT16 maxMsgSize)
{LosQueueCB *queueCB = NULL;UINT32 intSave;LOS_DL_LIST *unusedQueue = NULL;UINT8 *queue = NULL;UINT16 msgSize;(VOID)queueName;(VOID)flags;if (queueID == NULL) {return LOS_ERRNO_QUEUE_CREAT_PTR_NULL;}if (maxMsgSize > (OS_NULL_SHORT - sizeof(UINT32))) {// maxMsgSize上限 为啥要减去 sizeof(UINT32) ,因为前面存的是队列的大小return LOS_ERRNO_QUEUE_SIZE_TOO_BIG;}if ((len == 0) || (maxMsgSize == 0)) {return LOS_ERRNO_QUEUE_PARA_ISZERO;}msgSize = maxMsgSize + sizeof(UINT32);//总size = 消息体内容长度 + 消息大小(UINT32) /** Memory allocation is time-consuming, to shorten the time of disable interrupt,* move the memory allocation to here.*///内存分配非常耗时,为了缩短禁用中断的时间,将内存分配移到此处,用的时候分配队列内存queue = (UINT8 *)LOS_MemAlloc(m_aucSysMem1, (UINT32)len * msgSize);//从系统内存池中分配,由这里提供读写队列的内存if (queue == NULL) {//这里是一次把队列要用到的所有最大内存都申请下来了,能保证不会出现后续使用过程中内存不够的问题出现return LOS_ERRNO_QUEUE_CREATE_NO_MEMORY;//调用处有 OsSwtmrInit sys_mbox_new DoMqueueCreate ==}SCHEDULER_LOCK(intSave);if (LOS_ListEmpty(&g_freeQueueList)) {//没有空余的队列ID的处理,注意软时钟定时器是由 g_swtmrCBArray统一管理的,里面有正在使用和可分配空闲的队列SCHEDULER_UNLOCK(intSave);//g_freeQueueList是管理可用于分配的队列链表,申请消息队列的ID需要向它要OsQueueCheckHook();(VOID)LOS_MemFree(m_aucSysMem1, queue);//没有就要释放 queue申请的内存return LOS_ERRNO_QUEUE_CB_UNAVAILABLE;}unusedQueue = LOS_DL_LIST_FIRST(&g_freeQueueList);//找到一个没有被使用的队列LOS_ListDelete(unusedQueue);//将自己从g_freeQueueList中摘除, unusedQueue只是个 LOS_DL_LIST 结点.queueCB = GET_QUEUE_LIST(unusedQueue);//通过unusedQueue找到整个消息队列(LosQueueCB)queueCB->queueLen = len;	//队列中消息的总个数,注意这个一旦创建是不能变的.queueCB->queueSize = msgSize;//消息节点的大小,注意这个一旦创建也是不能变的.queueCB->queueHandle = queue;	//队列句柄,队列内容存储区. queueCB->queueState = OS_QUEUE_INUSED;	//队列状态使用中queueCB->readWriteableCnt[OS_QUEUE_READ] = 0;//可读资源计数,OS_QUEUE_READ(0):可读.queueCB->readWriteableCnt[OS_QUEUE_WRITE] = len;//可些资源计数 OS_QUEUE_WRITE(1):可写, 默认len可写.queueCB->queueHead = 0;//队列头节点queueCB->queueTail = 0;//队列尾节点LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_READ]);//初始化可读队列链表LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_WRITE]);//初始化可写队列链表LOS_ListInit(&queueCB->memList);//OsQueueDbgUpdateHook(queueCB->queueID, OsCurrTaskGet()->taskEntry);//在创建或删除队列调试信息时更新任务条目SCHEDULER_UNLOCK(intSave);*queueID = queueCB->queueID;//带走队列IDreturn LOS_OK;
}

解读

  • 创建和初始化一个LosQueueCB
  • 动态分配内存来保存消息内容,LOS_MemAlloc(m_aucSysMem1, (UINT32)len * msgSize);
  • msgSize = maxMsgSize + sizeof(UINT32);头四个字节放消息的长度,但消息最大长度不能超过maxMsgSize
  • readWriteableCnt记录读/写队列的数量,独立计算
  • readWriteList挂的是等待读取队列的任务链表 将在OsTaskWait(&queueCB->readWriteList[readWrite], timeout, TRUE);中将任务挂到链表上.

关键函数OsQueueOperate

队列的读写都要经过 OsQueueOperate

/************************************************
队列操作.是读是写由operateType定
本函数是消息队列最重要的一个函数,可以分析出读取消息过程中
发生的细节,涉及任务的唤醒和阻塞,阻塞链表任务的相互提醒.
************************************************/
UINT32 OsQueueOperate(UINT32 queueID, UINT32 operateType, VOID *bufferAddr, UINT32 *bufferSize, UINT32 timeout)
{LosQueueCB *queueCB = NULL;LosTaskCB *resumedTask = NULL;UINT32 ret;UINT32 readWrite = OS_QUEUE_READ_WRITE_GET(operateType);//获取读/写操作标识UINT32 intSave;SCHEDULER_LOCK(intSave);queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID);//获取对应的队列控制块ret = OsQueueOperateParamCheck(queueCB, queueID, operateType, bufferSize);//参数检查if (ret != LOS_OK) {goto QUEUE_END;}if (queueCB->readWriteableCnt[readWrite] == 0) {//根据readWriteableCnt判断队列是否有消息读/写if (timeout == LOS_NO_WAIT) {//不等待直接退出ret = OS_QUEUE_IS_READ(operateType) ? LOS_ERRNO_QUEUE_ISEMPTY : LOS_ERRNO_QUEUE_ISFULL;goto QUEUE_END;}if (!OsPreemptableInSched()) {//不支持抢占式调度ret = LOS_ERRNO_QUEUE_PEND_IN_LOCK;goto QUEUE_END;}//任务等待,这里很重要啊,将自己从就绪列表摘除,让出了CPU并发起了调度,并挂在readWriteList[readWrite]上,挂的都等待读/写消息的taskret = OsTaskWait(&queueCB->readWriteList[readWrite], timeout, TRUE);//任务被唤醒后会回到这里执行,什么时候会被唤醒?当然是有消息的时候!if (ret == LOS_ERRNO_TSK_TIMEOUT) {//唤醒后如果超时了,返回读/写消息失败ret = LOS_ERRNO_QUEUE_TIMEOUT;goto QUEUE_END;//}} else {queueCB->readWriteableCnt[readWrite]--;//对应队列中计数器--,说明一条消息只能被读/写一次}OsQueueBufferOperate(queueCB, operateType, bufferAddr, bufferSize);//发起读或写队列操作if (!LOS_ListEmpty(&queueCB->readWriteList[!readWrite])) {//如果还有任务在排着队等待读/写入消息(当时不能读/写的原因有可能当时队列满了==)resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->readWriteList[!readWrite]));//取出要读/写消息的任务OsTaskWake(resumedTask);//唤醒任务去读/写消息啊SCHEDULER_UNLOCK(intSave);LOS_MpSchedule(OS_MP_CPU_ALL);//让所有CPU发出调度申请,因为很可能那个要读/写消息的队列是由其他CPU执行LOS_Schedule();//申请调度return LOS_OK;} else {queueCB->readWriteableCnt[!readWrite]++;//对应队列读/写中计数器++}QUEUE_END:SCHEDULER_UNLOCK(intSave);return ret;
}

解读

  • queueID 指定操作消息队列池中哪个消息队列
  • operateType 表示本次是是读/写消息
  • bufferAddrbufferSize表示如果读操作,用buf接走数据,如果写操作,将buf写入队列.
  • timeout只用于当队列中没有读/写内容时的等待.
    • 当读消息时发现队列中没有可读的消息,此时timeout决定是否将任务挂入等待读队列任务链表
    • 当写消息时发现队列中没有空间用于写的消息,此时timeout决定是否将任务挂入等待写队列任务链表
  • if (!LOS_ListEmpty(&queueCB->readWriteList[!readWrite]))最有意思的是这行代码.
    • 在一次读消息完成后会立即唤醒写队列任务链表的任务,因为读完了就有了剩余空间,等待写队列的任务往往是因为没有空间而进入等待状态.
    • 在一次写消息完成后会立即唤醒读队列任务链表的任务,因为写完了队列就有了新消息,等待读队列的任务往往是因为队列中没有消息而进入等待状态.

编程实例

创建一个队列,两个任务。任务1调用写队列接口发送消息,任务2通过读队列接口接收消息。

  • 通过LOS_TaskCreate创建任务1和任务2。
  • 通过LOS_QueueCreate创建一个消息队列。
  • 在任务1 send_Entry中发送消息。
  • 在任务2 recv_Entry中接收消息。
  • 通过LOS_QueueDelete删除队列。
#include "los_task.h"
#include "los_queue.h"static UINT32 g_queue;
#define BUFFER_LEN 50VOID send_Entry(VOID)
{UINT32 i = 0;UINT32 ret = 0;CHAR abuf[] = "test is message x";UINT32 len = sizeof(abuf);while (i < 5) {abuf[len -2] = '0' + i;i++;ret = LOS_QueueWriteCopy(g_queue, abuf, len, 0);if(ret != LOS_OK) {dprintf("send message failure, error: %x\n", ret);}LOS_TaskDelay(5);}
}VOID recv_Entry(VOID)
{UINT32 ret = 0;CHAR readBuf[BUFFER_LEN] = {0};UINT32 readLen = BUFFER_LEN;while (1) {ret = LOS_QueueReadCopy(g_queue, readBuf, &readLen, 0);if(ret != LOS_OK) {dprintf("recv message failure, error: %x\n", ret);break;}dprintf("recv message: %s\n", readBuf);LOS_TaskDelay(5);}while (LOS_OK != LOS_QueueDelete(g_queue)) {LOS_TaskDelay(1);}dprintf("delete the queue success!\n");
}UINT32 Example_CreateTask(VOID)
{UINT32 ret = 0; UINT32 task1, task2;TSK_INIT_PARAM_S initParam;initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)send_Entry;initParam.usTaskPrio = 9;initParam.uwStackSize = LOS_TASK_MIN_STACK_SIZE;initParam.pcName = "sendQueue";
#ifdef LOSCFG_KERNEL_SMPinitParam.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());
#endifinitParam.uwResved = LOS_TASK_STATUS_DETACHED;LOS_TaskLock();ret = LOS_TaskCreate(&task1, &initParam);if(ret != LOS_OK) {dprintf("create task1 failed, error: %x\n", ret);return ret;}initParam.pcName = "recvQueue";initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)recv_Entry;ret = LOS_TaskCreate(&task2, &initParam);if(ret != LOS_OK) {dprintf("create task2 failed, error: %x\n", ret);return ret;}ret = LOS_QueueCreate("queue", 5, &g_queue, 0, BUFFER_LEN);if(ret != LOS_OK) {dprintf("create queue failure, error: %x\n", ret);}dprintf("create the queue success!\n");LOS_TaskUnlock();return ret;
}

结果验证

create the queue success!
recv message: test is message 0
recv message: test is message 1
recv message: test is message 2
recv message: test is message 3
recv message: test is message 4
recv message failure, error: 200061d
delete the queue success!

总结

  • 消息队列解决任务间大数据的传递
  • 以一种异步,解耦的方式实现任务通讯
  • 全局由消息队列池统一管理
  • 在创建消息队列时申请内存块存储消息内存.
  • 读/写操作统一管理,分开执行,A任务 读/写完消息后会立即唤醒等待写/读的B任务.

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

相关文章:

鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据

基本概念 队列又称消息队列&#xff0c;是一种常用于任务间通信的数据结构。队列接收来自任务或中断的不固定长度消息&#xff0c;并根据不同的接口确定传递的消息是否存放在队列空间中。 任务能够从队列里面读取消息&#xff0c;当队列中的消息为空时&#xff0c;挂起读取任务…...

Sentinel流量防卫兵

1、分布式服务遇到的问题 服务可用性问题 服务可用性场景 服务雪崩效应 因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程&#xff0c;就叫服务雪崩效应导致服务不可用的原因&#xff1a; 在服务提供者不可用的时候&#xff0c;会出现大量重试的情况&…...

微信小程序:14.什么是wxs,wxs的使用

wxs是小程序独有的一套脚本语言&#xff0c;结合wxml&#xff0c;可以构建出页面的结构 wxs的应用场景 wxml中无法调用在页面的js中定义的函数&#xff0c;但是wxml可以调用wxs中定义的函数。因此小程序中wxs的典型应用场景就是过滤器 wxs和js的关系 wxs有自己的数据类型 …...

Django运行不提示网址问题

问题描述&#xff1a;运行django项目不提示网址信息&#xff0c;也就是web没有起来&#xff0c;无法访问。 (my-venv-3.8) PS D:\Project\MyGitCode\public\it_blog\blog> python .\manage.py runserver INFO autoreload 636 Watching for file changes with StatReloader …...

web安全---xss漏洞/beef-xss基本使用

what xss漏洞----跨站脚本攻击&#xff08;Cross Site Scripting&#xff09;&#xff0c;攻击者在网页中注入恶意脚本代码&#xff0c;使受害者在浏览器中运行该脚本&#xff0c;从而达到攻击目的。 分类 反射型---最常见&#xff0c;最广泛 用户将带有恶意代码的url打开&a…...

第一天学习(GPT)

1.图片和语义是如何映射的&#xff1f; **Dalle2&#xff1a;**首先会对图片和语义进行预训练&#xff0c;将二者向量存储起来&#xff0c;然后将语义的vector向量转成图片的向量&#xff0c;然后基于这个图片往回反向映射&#xff08;Diffusion&#xff09;——>根据这段描…...

【C++之AVL树旋转操作的详细图解】

C++学习笔记---022 C++之AVL树旋转操作的详细图解1、AVL树的简单介绍1.1、基本概念1.2、平衡因子1.3、AVL树的特性2、C++中pair的介绍2.1、定义和初始化2.2、访问元素2.3、作为容器的元素2.4、作为函数的返回值3、AVL树节点的定义4、AVL的插入规则探究5、AVL树的旋转操作5.1、R…...

制作Android分区镜像

1 python生成一个sector数据 def get_oem_bootmode(): # Header size SECTOR_SIZE_IN_BYTES 512 header [0 for i in \ range(SECTOR_SIZE_IN_BYTES)] # magic # The ord() built-in function in # Python converts a character # into …...

如何代码激活service——packageKit 系统更新番外

在访问packageKit服务的过程中&#xff0c;服务一直访问失败&#xff0c;PackageKit::Daemon::global()->isRunning() 一直返回false&#xff0c;他是一个用于检查 PackageKit 守护进程是否正在运行的函数调用。在 Qt 和 PackageKit 的集成中&#xff0c;isRunning 方法通常…...

音视频常用工具

VLC 播放器简介 VLC 播放器 VLC支持多种常见音视频格式&#xff0c;支持多种流媒体传输协议&#xff0c;也可当作本地流媒体服务器使用&#xff0c;功能十分强大。官网下载地址: https://www.videolan.org/ VLC media player VLC 是一款自由、开源的跨平台多媒体播放器及框架&…...

周刊是聪明人筛选优质知识的聪明手段!

这是一个信息过载的时代&#xff0c;也是一个信息匮乏的时代。 这种矛盾的现象在 Python 编程语言上的表现非常明显。 它是常年高居编程语言排行榜的最流行语言之一&#xff0c;在国外发展得如火如荼&#xff0c;开发者、项目、文章、播客、会议活动等相关信息如海如潮。 但…...

设计模式Java实现-建造者模式

楔子 小七在2019年的时候&#xff0c;就想写一个关于设计模式的专栏&#xff0c;但是最终却半途而废了。粗略一想&#xff0c;如果做完一件事要100分钟&#xff0c;小七用3分钟热情做的事&#xff0c;最少也能完成10件事情了。所以这一次&#xff0c;一定要把他做完&#xff0…...

微博视频怎么下载无水印

在当今社交媒体时代&#xff0c;微博已经成为人们获取信息、分享生活的重要平台之一。许多人在浏览微博时常常遇到一个问题&#xff1a;如何下载微博视频而不留下烦人的水印呢?今天&#xff0c;我将分享一些神秘的方法&#xff0c;让你轻松解锁微博视频的无水印下载技巧。 第…...

为什么要梯度累积

文章目录 梯度累积什么是梯度累积如何理解理解梯度累积梯度累积的工作原理 梯度累积的数学原理梯度累积过程如何实现梯度累积 梯度累积的可视化 梯度累积 什么是梯度累积 随着深度学习模型变得越来越复杂&#xff0c;模型的训练通常需要更多的计算资源&#xff0c;特别是在训…...

知识图谱在提升大语言模型性能中的应用:减少幻觉与增强推理的综述

幻觉现象指的是模型在生成文本时可能会产生一些听起来合理但实际上并不准确或相关的输出&#xff0c;这主要是由于模型在训练数据中存在知识盲区所致。 为了解决这一问题&#xff0c;研究人员采取了多种策略&#xff0c;其中包括利用知识图谱作为外部信息源。知识图谱通过将信息…...

P8800 [蓝桥杯 2022 国 B] 卡牌

P8800 [蓝桥杯 2022 国 B] 卡牌 分析 “最多” -- 二分 1.二分区间&#xff08;凑齐的卡牌套数&#xff09;&#xff1a; l&#xff1a;a[]min&#xff1b;r&#xff1a;(a[]b[])max 2.check(x)&#xff1a; &#xff08;1&#xff09;for循环内&#xff1a; 判断x - a[i…...

MySQL商城数据表(80-84)

80商品规格值表 DROP TABLE IF EXISTS niumo_spec_items; CREATE TABLE niumo_spec_items (itemId int(11) NOT NULL AUTO_INCREMENT COMMENT 自增ID,shopId int(11) NOT NULL DEFAULT 0 COMMENT 店铺ID,catId int(11) NOT NULL DEFAULT 0 COMMENT 类型ID,goodsId int(11) NOT…...

使用Gitbook生成电子书

背景 《Google工程实践文档》相对原文Google’s Engineering Practices documentation &#xff0c;部分内容过时了。需要更新中文版&#xff0c;并使用Gitbook把Markdown文件转换成对应的PDF电子书。   上一次生成PDF电子书是5年前&#xff0c;当时生成电子书的环境早已不在…...

设计模式之传输对象模式

在编程江湖里&#xff0c;有一种模式&#xff0c;它如同数据的“特快专递”&#xff0c;穿梭于系统间&#xff0c;保证信息的快速准确送达&#xff0c;它就是——传输对象模式&#xff08;Data Transfer Object, DTO&#xff09;。这不仅仅是数据的搬运工&#xff0c;更是提升系…...

Re69:读论文 LaMDA: Language Models for Dialog Applications

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文名称&#xff1a;LaMDA: Language Models for Dialog Applications ArXiv网址&#xff1a;https://arxiv.org/abs/2201.08239 本文介绍谷歌提出的对话大模型LaMDA&#xff0c;主要关注对各项指标&#x…...

算法学习:二分查找

&#x1f525; 引言 在现代计算机科学与软件工程的实践中&#xff0c;高效数据检索是众多应用程序的核心需求之一。二分查找算法&#xff0c;作为解决有序序列查询问题的高效策略&#xff0c;凭借其对数时间复杂度的优越性能&#xff0c;占据着算法领域里举足轻重的地位。本篇内…...

github提交代码失败解决方案

1.打开github.push 工具 ​ 如果未安装github客户端请参考附录github 安装配置 2.设置Git的user name和email git config --global user.name "yourname" git config --global user.email "youremail" 3.生成SSH密钥 查看是否已经有了ssh密钥&#xff1…...

连锁收银系统总仓到门店库存调拨操作教程

1、进入系统后台&#xff0c;系统后台登录网址&#xff1a; 2、点击商品>门店调拨 3、选择调出仓库和调入门店 4、可选择添加商品逐个进行调拨&#xff0c;也可以批量导入需要调拨的商品 然后点击确定。 5、新增调拨后&#xff0c;系统会显示“待出库”状态 6、仓库已经准备…...

公网tcp转流

之前做过几次公网推流的尝试, 今天试了UDP推到公网, 再用TCP从公网拉下来, 发现不行, 就直接改用TCP转TCP了. 中间中转使用的python脚本, 感谢GPT提供技术支持: import socket import threadingdef tcp_receiver(port, forward_queue):"""接收TCP数据并将其放入…...

【Linux 基础 IO】文件系统

文章目录 1.初步理解文件2. fopen ( )的详解 1.初步理解文件 &#x1f427;① 打开文件&#xff1a; 本质是进程打开文件&#xff1b; &#x1f427;②文件没有被打开的时候在哪里呢&#xff1f; ----- 在磁盘中&#xff1b; &#x1f427;③进程可以打开很多个文件吗&#xff…...

Chrome浏览器安装React工具

一、如果网络能访问Google商店&#xff0c;直接安装官方插件即可 二、网络不能访问Google商店&#xff0c;使用安装包进行安装 1、下载react工具包 链接&#xff1a;https://pan.baidu.com/s/1qAeqxSafOiNV4CG3FVVtTQ 提取码&#xff1a;vgwj 2、chrome浏览器安装react工具…...

React常用组件分享

1、轮播组件&#xff1a; React Awesome Slider React Slider Carousel Component - react-awesome-slider...

JSON原生AJAX

文章目录 JSONFastjsonfastjson引入fastjson 常用APIfastjson作用常用API使用实例 ajax和json综合(重要)请求参数和响应数据都是普通字符串响应数据改为json格式请求和响应都是js数据封装到Result类和抽取到BaseController 原生AjaxAJAX的执行流程XMLHttpRequest对象使用原生的…...

Go图片列表

需求 在一个页面浏览目录下所有图片 代码 package mainimport ("net/http""fmt""io/ioutil""sort""strings""strconv""net/url" )func handleRequest(w http.ResponseWriter, r *http.Request) { de…...

1.4 初探JdbcTemplate操作

实战目的 掌握Spring框架中JdbcTemplate的使用&#xff0c;实现对数据库的基本操作。理解数据库连接池的工作原理及其在实际开发中的重要性。通过实际操作&#xff0c;加深对Spring框架中ORM&#xff08;对象关系映射&#xff09;的理解。 关键技术点 JdbcTemplate操作&…...

门窗 东莞网站建设/精准引流的网络推广

定义函数&#xff0c;实现打印任意区间内偶数 function printEven1(min,max) {for (var i min; i <max ; i) {if (i%20){console.log(i)}}}printEven1(50,60)//可以给参数默认值function printEven2(min1,max30) {for (var i min; i <max ; i) {if (i%20){console.log(…...

中国光大国际建设工程公司网站/平台推广销售话术

最近喜欢用上了DataTable对数据的处理&#xff0c;感觉DataTable很强大。。。不用再在查询语句中进行处理sql语句。呵呵。这就懒人的好出&#xff0c; 好了废话不多说。还是把我我最近用到关于DataTable的排序功能贴出来&#xff0c;方便自己&#xff0c;也方便其他同学使用&am…...

网站页面配色分析/百度推广优化排名

利用Map来存放数据&#xff0c;利用List来存放Map&#xff0c;两者相互结合&#xff0c;下面是一个简单的例子。 ORM思想&#xff1a;对象关系映射 ORM思想的简单实验&#xff1a;map表示一行数据&#xff0c;多行数据是多个map&#xff1b;将多个map放到list中 上代码&#…...

宣传型企业网站设计方案/网站服务器ip查询

可以使用 filter() 函数和一个自定义函数来过滤多个字典。自定义函数需要接受一个字典作为参数,并根据需要返回 True 或 False。然后将该函数作为第一个参数传递给 filter() 函数,将字典列表作为第二个参数传递给 filter() 函数,即可得到过滤后的字典列表。 举个例子: def …...

宁波网站关键词优化公司/百度关键词搜索广告的优缺点

【原文】 Cache的基本用途 提到Cache&#xff0c;不得不说说它的主要功能&#xff1a;改善程序性能。 ASP.NET是一种动态页面技术&#xff0c;用ASP.NET技术做出来的网页几乎都是动态的&#xff0c;所谓动态是指&#xff1a;页面的内容会随着不同的用户或者持续更新的数据&…...

合肥网站建设制作/北京搜索优化推广公司

咸鱼ZTMS实例—智能车接线“新手玩家”要注意看接线说明哦~要不就 基础接线 传感器上含有“VCC”字样的一般接到开发板的3.3V&#xff0c;5V或者12V。&#xff08;看传感器参数&#xff09;传感器上含有"GND"字样的直接接到开发板的GND即可其他引脚接线看说明即可例…...