AI大模型编写多线程并发框架(六十三):监听器优化·上

系列文章目录


文章目录

  • 系列文章目录
  • 前言
    • 一、项目背景
    • 二、第七轮对话-补充异步执行代码
    • 三、第八轮对话-增加累计完成等字眼
    • 四、第九轮对话-线程安全优化
    • 五、第十轮对话-增加运行时数据
    • 七、参考文章


前言

在这个充满技术创新的时代,AI大模型正成为开发者们的新宠。它们可以帮助我们完成从简单的问答到复杂的编程任务,所以AI编程将会是未来的主流方向,利用AI大模型的能力,本文将介绍从零到一用AI大模型编写一个多线程并发框架。

一、项目背景

经过上两篇文章和AI的对话,我们基本捣鼓出来了多线程并发框架的雏形,接下来是比较重要的监控模块,本文将会通过比较长的篇幅来叙述。

本多线程框架使用示例如下:源码地址
1、引入依赖。

<dependency><groupId>io.github.vipjoey</groupId><artifactId>mmc-juc</artifactId><version>1.0</version>
</dependency>

2、使用示例。


// 创建一个MmcTaskExecutor实例,用于执行单次长耗时任务
// 下面是创建一个计算从1加到100的任务,总共100个任务,采用fork分治算法,阈值为10,总共任务为100 / 10 * 2 = 20个大任务,执行速率约为10/s
MmcTaskExecutor<Integer, Integer> mmcTaskExecutor = MmcTaskExecutor.<Integer, Integer>builder().taskSource(IntStream.rangeClosed(1, 100).boxed().collect(Collectors.toList())) // 设置任务源.taskProcessor(x -> x.stream().reduce(0, Integer::sum)) // 设置任务处理方法.taskMerger(Integer::sum) // 设置结果处理方法(可选).threshold(10) // 设置任务处理阈值(可选).taskName("mmcTaskExample") // 设置任务名称.rateLimiter(10, 20)  // 设置速率限制,容量为10,每秒产生令牌为20,休眠时间为10ms.forkJoinPoolConcurrency(4) // 设置ForkJoinPool的并发度为4.build();// 同步执行并打印结果        
System.out.println("result: " + mmcTaskExecutor.execute());// 任务执行过程监控
[mmcTaskExample] Tasks submitted. Total tasks: 100
[mmcTaskExample] Task started. Completed tasks: 14, remaining tasks: 86
[mmcTaskExample] Task started. Completed tasks: 14, remaining tasks: 86
[mmcTaskExample] Task started. Completed tasks: 20, remaining tasks: 80
[mmcTaskExample] Task started. Completed tasks: 26, remaining tasks: 74
[mmcTaskExample] Task started. Completed tasks: 33, remaining tasks: 67
[mmcTaskExample] Task started. Completed tasks: 40, remaining tasks: 60
[mmcTaskExample] Task started. Completed tasks: 46, remaining tasks: 54
[mmcTaskExample] Task started. Completed tasks: 52, remaining tasks: 48
[mmcTaskExample] Task started. Completed tasks: 58, remaining tasks: 42
[mmcTaskExample] Task started. Completed tasks: 64, remaining tasks: 36
[mmcTaskExample] Task started. Completed tasks: 70, remaining tasks: 30
[mmcTaskExample] Task started. Completed tasks: 76, remaining tasks: 24
[mmcTaskExample] Task started. Completed tasks: 82, remaining tasks: 18
[mmcTaskExample] Task started. Completed tasks: 88, remaining tasks: 12
[mmcTaskExample] Task started. Completed tasks: 94, remaining tasks: 6
[mmcTaskExample] Task started. Completed tasks: 100, remaining tasks: 0
[mmcTaskExample] Tasks completed. Elapsed time: 1107 ms 
result: 5050

二、第七轮对话-补充异步执行代码

1、参考限流器的对话,我们知道AI是不会主动帮忙抽象公共接口的,所以这里我们干脆要求AI给我们定义好监听器接口,方便后续扩展。
在这里插入图片描述

2、下面是AI给的回复。

  • 【为了实现监听器功能,我们首先需要创建一个监听器接口MmcTaskListener,然后在MmcTaskExecutor类中使用它。我们还将提供一个默认实现DefaultMmcTaskListener,并允许用户通过Builder类替换监听器。首先,我们创建一个MmcTaskListener接口。】
public interface MmcTaskListener {void onTaskStarted(int totalTasks, int remainingTasks);void onTaskCompleted(int totalTasks, int remainingTasks, long elapsedTime);
}
  • 【接下来,我们创建一个默认的监听器实现DefaultMmcTaskListener。】
public class DefaultMmcTaskListener implements MmcTaskListener {@Overridepublic void onTaskStarted(int totalTasks, int remainingTasks) {System.out.println("Task started. Total tasks: " + totalTasks + ", remaining tasks: " + remainingTasks);}@Overridepublic void onTaskCompleted(int totalTasks, int remainingTasks, long elapsedTime) {System.out.println("Task completed. Total tasks: " + totalTasks + ", remaining tasks: " + remainingTasks + ", elapsed time: " + elapsedTime + " ms");}
}
  • 【然后,我们修改MmcTaskExecutor类以使用MmcTaskListener接口。】
import java.util.List;
import java.util.concurrent.ForkJoinPool;public class MmcTaskExecutor<T, R> {// ...省略其他成员变量private MmcTaskListener taskListener;private MmcTaskExecutor(Builder<T, R> builder) {// ...省略其他初始化代码this.taskListener = builder.taskListener;}// ...省略其他方法public static class Builder<T, R> {// ...省略其他成员变量private MmcTaskListener taskListener = new DefaultMmcTaskListener(); // 默认使用DefaultMmcTaskListener// ...省略其他方法public Builder<T, R> taskListener(MmcTaskListener taskListener) {this.taskListener = taskListener;return this;}// ...省略其他方法}
}
  • 【最后,我们需要修改MmcTask类以调用监听器的方法。】
import java.util.List;
import java.util.concurrent.RecursiveTask;public class MmcTask<T, R> extends RecursiveTask<R> {// ...省略其他成员变量private MmcTaskListener taskListener;public MmcTask(List<T> taskSource, MmcTaskProcessor<T, R> taskProcessor, MmcTaskMerger<R> taskMerger, int threshold, int start, int end, RateLimiter rateLimiter, MmcTaskListener taskListener) {// ...省略其他初始化代码this.taskListener = taskListener;}@Overrideprotected R compute() {// ...省略其他代码// 在处理任务之前调用onTaskStarted方法taskListener.onTaskStarted(taskSource.size(), end - start);// ...省略其他代码// 在任务完成后调用onTaskCompleted方法long elapsedTime = System.currentTimeMillis() - startTime;taskListener.onTaskCompleted(taskSource.size(), end - start - 1, elapsedTime);// ...省略其他代码}
}

本轮AI的回复有些欠缺,它并没有GET到我的诉求,可能跟我的提问方式有关。实际上我需要监控的是TaskSource集合被执行完成的进度,AI只是监控了compute方法,也就是监控每个阈值下的小任务的执行,这明显是错的。

三、第八轮对话-增加累计完成等字眼

1、由于上一轮对话,可能是提问方式不对,我重新增加【首次】、【累计】这些字眼,希望AI能理解我的意思。
在这里插入图片描述

2、下面是AI大模型给的回复。

  • 【为了满足您的要求,我们需要修改MmcTaskListener接口以添加新的事件方法,然后修改DefaultMmcTaskListener以实现这些方法。接下来,我们将在MmcTaskExecutor类和MmcTask类中调用这些方法。首先,我们修改MmcTaskListener接口以添加新的事件方法。】
public interface MmcTaskListener {void onTasksSubmitted(int totalTasks, long startTime);void onTaskStarted(int completedTasks, int remainingTasks);void onTasksCompleted(long elapsedTime, long endTime);
}
  • 【接下来,我们修改DefaultMmcTaskListener以实现新的事件方法。】
public class DefaultMmcTaskListener implements MmcTaskListener {@Overridepublic void onTasksSubmitted(int totalTasks, long startTime) {System.out.println("Tasks submitted. Total tasks: " + totalTasks + ", start time: " + startTime);}@Overridepublic void onTaskStarted(int completedTasks, int remainingTasks) {System.out.println("Task started. Completed tasks: " + completedTasks + ", remaining tasks: " + remainingTasks);}@Overridepublic void onTasksCompleted(long elapsedTime, long endTime) {System.out.println("Tasks completed. Elapsed time: " + elapsedTime + " ms, end time: " + endTime);}
}
  • 【然后,我们需要修改MmcTaskExecutor类以调用onTasksSubmitted和onTasksCompleted方法。】
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicInteger;public class MmcTaskExecutor<T, R> {// ...省略其他成员变量和方法private AtomicInteger completedTasks = new AtomicInteger(); // 添加一个原子整数以跟踪已完成的任务数量// 同步执行并返回结果public R execute() {MmcTask<T, R> mmcTask = new MmcTask<>(taskSource, taskProcessor, taskMerger, threshold, 0, taskSource.size(), rateLimiter, taskListener, completedTasks);// 调用onTasksSubmitted方法taskListener.onTasksSubmitted(taskSource.size(), System.currentTimeMillis());R result = forkJoinPool.invoke(mmcTask);// 调用onTasksCompleted方法taskListener.onTasksCompleted(mmcTask.getElapsedTime(), System.currentTimeMillis());return result;}// ...省略其他方法
}
  • 【最后,我们需要修改MmcTask类以调用onTaskStarted方法。】
import java.util.List;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicInteger;public class MmcTask<T, R> extends RecursiveTask<R> {// ...省略其他成员变量和方法private AtomicInteger completedTasks;public MmcTask(List<T> taskSource, MmcTaskProcessor<T, R> taskProcessor, MmcTaskMerger<R> taskMerger, int threshold, int start, int end, RateLimiter rateLimiter, MmcTaskListener taskListener, AtomicInteger completedTasks) {// ...省略其他初始化代码this.completedTasks = completedTasks;}@Overrideprotected R compute() {// 在处理任务之前调用onTaskStarted方法taskListener.onTaskStarted(completedTasks.get(), end - start);// ...省略其他代码// 在任务完成后更新已完成任务的计数completedTasks.incrementAndGet();// ...省略其他代码}
}

从本次AI大模型的回复来看,存在以下问题:

  • 它似乎不理解任务拆分前后计数的区别,实际上后续我尝试了N种问法,它都不理解,它理解的任务累计完成个数就是completedTasks.incrementAndGet();而不是我想要的taskRuntime.getCompletedTasks().addAndGet(end - start);
  • 它不会主动发现线程安全问题,这个跟我们提问的要求有关,毕竟是生成式AI,所以它不会考虑任务提交时需要重置AtomicInteger。
  • 同理,它的剩余任务个数的计算方式也是错误的,它的答案是end - start,而不是我想要的taskSource.size() - 累计完成任务个数

不过,这些都是小问题,不用太纠结,我们继续提诉求去优化。

四、第九轮对话-线程安全优化

1、从AI大模型给出的代码来看,是没有考虑线程安全问题的,我们需要补充一下诉求:
在这里插入图片描述

2、下面是AI的回复。

  • 【为了满足您的要求,我们将对MmcTaskExecutor类进行修改以确保线程安全,并在每次提交任务时重置计数器。我们将使用原子整数AtomicInteger来实现线程安全的计数器。首先,我们将修改MmcTaskExecutor类以添加一个原子整数completedTasks以跟踪已完成的任务数量。我们还将在每次执行任务时重置completedTasks。】
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicInteger;public class MmcTaskExecutor<T, R> {// ...省略其他成员变量和方法private AtomicInteger completedTasks = new AtomicInteger(); // 添加一个原子整数以跟踪已完成的任务数量// 同步执行并返回结果public R execute() {completedTasks.set(0); // 重置已完成任务的计数器MmcTask<T, R> mmcTask = new MmcTask<>(taskSource, taskProcessor, taskMerger, threshold, 0, taskSource.size(), rateLimiter, taskListener, completedTasks);// 调用onTasksSubmitted方法taskListener.onTasksSubmitted(taskSource.size(), System.currentTimeMillis());R result = forkJoinPool.invoke(mmcTask);// 调用onTasksCompleted方法taskListener.onTasksCompleted(mmcTask.getElapsedTime(), System.currentTimeMillis());return result;}// ...省略其他方法
}
  • 【接下来,我们将修改MmcTask类以确保线程安全并调用监听器的方法。】
import java.util.List;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicInteger;public class MmcTask<T, R> extends RecursiveTask<R> {// ...省略其他成员变量和方法private AtomicInteger completedTasks;public MmcTask(List<T> taskSource, MmcTaskProcessor<T, R> taskProcessor, MmcTaskMerger<R> taskMerger, int threshold, int start, int end, RateLimiter rateLimiter, MmcTaskListener taskListener, AtomicInteger completedTasks) {// ...省略其他初始化代码this.completedTasks = completedTasks;}@Overrideprotected R compute() {// 在处理任务之前调用onTaskStarted方法taskListener.onTaskStarted(completedTasks.get(), end - start);// ...省略其他代码// 在任务完成后更新已完成任务的计数int completed = completedTasks.incrementAndGet();// 调用onTaskStarted方法,以便在每个小任务完成时更新已完成任务的计数taskListener.onTaskStarted(completed, end - start - completed);// ...省略其他代码}
}

这次AI大模型的回复也是不尽人意,如果多个MmcTask任务同时执行的话,计数器是不准确的,我们继续耐心优化一下我们的提示词。

五、第十轮对话-增加运行时数据

1、由于我们设计的初衷,任务可以多次提交的。所以每个任务运行时应该产生运行时数据,我们监控的也是运行时数据,运行时数据需要一个唯一标识,也就是任务名称来与之关联。另外,由于MmcTask的构造函数过长,我在其它对话中让它优化过了,这里就不详细讲述。
在这里插入图片描述

2、以下是AI的回复。

  • 【为了满足您的要求,我们可以创建一个新的类TaskRuntime,它将包含任务名称、任务源总数、已完成任务数和剩余任务数等信息。然后,我们将修改MmcTask和MmcTaskListener以使用TaskRuntime。首先,我们创建一个新的类TaskRuntime。】
public class TaskRuntime {private String taskName;private int totalTasks;private AtomicInteger completedTasks;private AtomicInteger remainingTasks;public TaskRuntime(String taskName, int totalTasks) {this.taskName = taskName;this.totalTasks = totalTasks;this.completedTasks = new AtomicInteger();this.remainingTasks = new AtomicInteger(totalTasks);}// ...省略getter和setter方法
}
  • 【接下来,我们修改MmcTaskListener接口以使用TaskRuntime。】
public interface MmcTaskListener {void onTasksSubmitted(TaskRuntime taskRuntime, long startTime);void onTaskStarted(TaskRuntime taskRuntime);void onTasksCompleted(TaskRuntime taskRuntime, long elapsedTime, long endTime);
}
  • 【然后,我们修改MmcTask以使用TaskRuntime。】
import java.util.List;
import java.util.concurrent.RecursiveTask;public class MmcTask<T, R> extends RecursiveTask<R> {// ...省略其他成员变量和方法private TaskRuntime taskRuntime;public MmcTask(Builder<T, R> builder) {// ...省略其他初始化代码this.taskRuntime = new TaskRuntime(builder.taskName, builder.taskSource.size());}@Overrideprotected R compute() {// 在处理任务之前调用onTaskStarted方法taskListener.onTaskStarted(taskRuntime);// ...省略其他代码// 在任务完成后更新已完成任务的计数taskRuntime.getCompletedTasks().incrementAndGet();taskRuntime.getRemainingTasks().decrementAndGet();// ...省略其他代码}
}
  • 【最后,我们修改MmcTaskExecutor类以使用TaskRuntime。】
import java.util.List;
import java.util.concurrent.ForkJoinPool;public class MmcTaskExecutor<T, R> {// ...省略其他成员变量和方法// 提交新任务public R submitTask(MmcTask<T, R> mmcTask) {// 调用onTasksSubmitted方法taskListener.onTasksSubmitted(mmcTask.getTaskRuntime(), System.currentTimeMillis());R result = forkJoinPool.invoke(mmcTask);// 调用onTasksCompleted方法taskListener.onTasksCompleted(mmcTask.getTaskRuntime(), mmcTask.getElapsedTime(), System.currentTimeMillis());return result;}// 同步执行并返回结果public R execute() {MmcTask<T, R> mmcTask = new MmcTask.Builder<T, R>()// ...省略其他方法调用.build();return submitTask(mmcTask);}
}

这轮AI的回答还是有问题,例如下面:

    public MmcTask(Builder<T, R> builder) {// ...省略其他初始化代码this.taskRuntime = new TaskRuntime(builder.taskName, builder.taskSource.size());}

它在MmcTask来初始化运行时数据,这明显是不对的,继续让它修改。下一篇,继续优化监听器 - -

七、参考文章

  • 《AI大模型编写多线程并发框架(六十一):从零开始搭建框架》
  • 《AI大模型编写多线程并发框架(六十二):限流和并发度优化》
  • 《AI大模型编写多线程并发框架(六十三):监听器优化·上》
  • 《AI大模型编写多线程并发框架(六十四):监听器优化·下》
  • 《AI大模型编写多线程并发框架(六十五):发布和应用》

加我加群(备注csdn)一起交流学习!更多干货下载、项目源码和大厂内推等着你

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MDK 复制hex文件到根目录并加上日期 bat 脚本

工程目录示例 copy.bat ::关闭命令行显示 echo off :: GBK chcp 936 >nul setlocal EnableDelayedExpansion:设置文件夹路径 set "FolderName.\Objects":: set "FolderName.\Objects" :: #;copy %FolderName%\*.hex ..\*.hex //复制到根目录 :: c…

uniapp小程序实现横屏手写签名

<template><view class"signBox column-me"><!-- 这个是自定义的title-可根据自己封装的title的作为调整 --><status-bar title"电子签名" :bgColor"null"></status-bar><view class"topHint">请…

滚雪球学MyBatis-Plus(02):环境准备

环境准备 本地开发环境参考如下&#xff1a; 开发工具&#xff1a;IntelliJ IDEA 2021.3.2JDK版本&#xff1a; JDK 1.8Spring Boot版本&#xff1a;2.3.1.RELEASEMaven版本&#xff1a;Apache Maven 3.8.2MySQL&#xff1a;5.6 前言 在上期内容中&#xff0c;我们系统地介绍了…

python实战三-提取Word数据到Excel

视频源码&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/83db5bb15383 一个文件夹下有大量会议通知文件&#xff0c;为word文件&#xff0c;文件格式都是一致的&#xff0c;现在要将文件中的一些字段提取出来汇总到Excel文件中。 会议通知文件格式如下&#xff1a; 要提…

电路笔记(PCB): 椭圆形板框绘制

使用 QCAD绘制并导出dxf文件 https://www.qcad.org/en/&#xff0c;QCAD 是一个免费的开源应用程序&#xff0c;用于二维 &#xff08;2D&#xff09; 计算机辅助绘图 &#xff08;CAD&#xff09;。 导入dxf文件

【Apache Doris】数据均衡问题排查指南

原文阅读&#xff1a;【巨人肩膀社区博客分享】【Apache Doris】数据均衡问题排查指南 一、前提概要 当集群处于以下几种情况时&#xff0c;可参考本文进行问题排查。 •BE节点之间的数据不均 •单个BE节点上的多个磁盘之间的数据不均 •BE节点的上线和下线进度卡死&#…

路由策略工具

1.产生背景 a.保证数据访问的安全性 b.提高链路带宽利用率 c.流量路径不优 2.解决方案: 解决方案一:路由策略:可通过修改路由条目(即对接收和发布的路由进行过滤)来控制流量可达性 解决方案二:流量过滤:可使用Traffic-Filter工具对数据直接进行过滤 3.路由策略工具 a.可利…

基于jstat 分析垃圾回收情况,进行JVM调优

文章目录 引言I jstat 统计信息工具JVM 堆内存布局命令格式元数据空间统计堆内存统计JVM编译方法统计编译统计类加载统计II JVM调优基本概念: 应用程序的响应时间(RT)和吞吐量(QPS)JVM调优原理调优思路调优方法JVM调优技巧建议III 基于jstat 分析垃圾回收情况,进行JVM调优…

gitee 简单使用

目录 1. github 介绍 2. gitee 介绍 2.1 下载工具 2.2 创建仓库 2.3 clone 远程仓库到本地 2.4 上传代码 2.4.1 add:预添加到本地仓库 2.4.2 commit:提交代码到仓库 2.4.3 push:推送添加操作到远程仓库 3. 问题解决 3.1 文件推送成功&#xff0c;gitee没有绿点 3.2…

如何实现OpenHarmony的OTA升级

OTA简介 随着设备系统日新月异&#xff0c;用户如何及时获取系统的更新&#xff0c;体验新版本带来的新的体验&#xff0c;以及提升系统的稳定性和安全性成为了每个厂商都面临的严峻问题。OTA&#xff08;Over the Air&#xff09;提供对设备远程升级的能力。升级子系统对用户…

.NET WPF 抖动动画

.NET WPF 抖动动画 文章目录 .NET WPF 抖动动画1. WPF 动画的基本概念2. 动画类型3. 使用XAML定义动画4. 使用代码后台定义动画5. 组合动画6. 动画控制7. 自定义动画8. 动画的性能优化9. Demo - 抖动动画1. WPF 动画的基本概念 动画(Animation):在WPF中,动画指的是在一段时…

三级_网络技术_53_应用题

一、 请根据下图所示网络结构回答下列问题。 1.设备1应选用__________网络设备。 2.若对整个网络实施保护&#xff0c;防火墙应加在图中位置1~3的__________位置上。 3.如果采用了入侵检测设备对进出网络的流量进行检测&#xff0c;并且探测器是在交换机1上通过端口镜像方式…

完整指南:CNStream流处理多路并发框架适配到NVIDIA Jetson Orin (一) 依赖库编译、第三方库编译安装

目录 1 jetson-ffmpeg的编译安装与配置--用来做视频编码、视频解码 2 CV-CUDA库的编译安装与配置--用来做图像缩放、裁剪、色域转换 3 cuda cudnn TensorRT相关库的拷贝与配置 3.1将cuda cudnn TensorRT相关的头文件拷贝到工程中 3.2 将cuda cudnn TensorRT相关的库拷贝到…

ArcGIS Pro技术应用

GIS是利用电子计算机及其外部设备&#xff0c;采集、存储、分析和描述整个或部分地球表面与空间信息系统。简单地讲&#xff0c;它是在一定的地域内&#xff0c;将地理空间信息和 一些与该地域地理信息相关的属性信息结合起来&#xff0c;达到对地理和属性信息的综合管理。GIS的…

学习之SQL语句之DQL(数据库查询语言)

DQL英文全称是Data Query Language(数据查询语言)&#xff0c;数据查询语言&#xff0c;用来查询数据库中表的记录 查询关键字:SELECT 数据准备&#xff1a; CREATE TABLE emp ( id INT COMMENT “编号”, workno VARCHAR ( 10 ) COMMENT “工号”, NAME VARCHAR ( 10 ) COMME…

day45.动态规划

1035.不相交的线: 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足&#xff1a; nums1[i] nums2[j] 且绘制的直线不与任何其他连线&#xff08;非水…

【Python】家庭用电数据的时序分析

Household Electricity Consumption | Kaggle 目录 数据简介 探索分析 数据清洗 用电占比 趋势分析 序列分解 周期分析 周期分解 分析小结 数据简介 240000-household-electricity-consumption-records数据集包含了一个家庭6个月的用电数据&#xff0c;收集于2007年1…

安防监控/软硬一体/视频汇聚网关EasyCVR硬件启动崩溃是什么原因?

安防视频监控EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。EasyCVR平台支持多种视频流的外部分发&#xff0c;如RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、WebRTC、WS-FMP4、HTTP-…

MySql【数据查询语言DQL】

DQL[非常重要] DQL 主要指查询语句,有查询单表数据,也有查多表数据表,单表查询 基本查询 条件查询 模糊查询 排序查询 聚合查询 去重查询 分组查询 限制查询 1、 数据准备 将发的stu.sql导入到MySql中 2、 基本查询 select 字段1,字段2,... from 表名; 查询返回的是一张…

C语言 ——— 文件读取结束的判定

目录 判定文件读取结束的方式 被错误使用的feof函数 判定文件结束的正确使用 判定文件读取结束的方式 判断文本文件是否读取结束&#xff1a; 利用 fgetc 判断返回值是否为 EOF 利用 fgets 判断返回值是否为 NULL 判断二进制文件是否读取结束&#xff1a; 利用 fread 判…