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

【Flutter入门到进阶】Dart进阶篇---Dart异步编程

1 并行与并发的编程区别

1.1 并发与并行

1.1.1 说明

        我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的并行车辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可以并行运行。

        CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数或者线程数就相当于并排可以通行的车道;而多个CPU  就相当于并排有多条高速公路,而每个高速公路并排有多个车道。

        当谈论并发的时候一定要加个单位时间,也就是说单位时间内并发量是多少?    

        离开了单位时间其实是没有意义的。俗话说,一心不能二用,这对计算机也一样,原则上一个 CPU 只能分配给一个进程,以便运行这个进程。我们通常使用的计算机中只有一个 CPU,也就是说只有一颗心,要让它一心多用同时运行多个进程,就必须使用并发技术。实现并发技术相当复杂,最容易理解的是“时间片轮转进程调度算法”。

        并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。

        并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。

1.1.2 图例

1.2 异步编程

1.2.1 分类

        1.利用资源的多线程编程方案

                并发编程

        2.利用单一资源,但是内部进行事件循环处理

                并行编程

1.2.2 并行的说明

        在并发的CPU理论中我们能知道,其实一个CPU只能承载一个线程处理

        但是通过时间片分配算法,让代码在宏观上看起来是异步的,但是微观上是同步的

1.3 并行异步开发思路

1.3.1 事件循环机制实现并行异步

                目的就是在主题中维护一个队列,然后循环队列

1.3.2 JAVA模拟示例

//Task
package com.kerwin.event;
public abstract class Task {     public abstract void run(); 
}//ScheduleMicrotask
package com.kerwin.event; 
public class ScheduleMicrotask extends Task {     @Override     public void run() {     } 
}//Timer
package com.kerwin.event; 
public class Timer extends Task {     @Override     public void run() {     } 
}//Main
package com.kerwin.event; 
import java.util.Queue; 
import java.util.concurrent.LinkedBlockingQueue; 
public class Main {     //微任务队列-优先级高     private static Queue<ScheduleMicrotask> scheduleMicrotasks = new LinkedBlockingQueue<>();     //事件队列-优先级低     private static Queue<Timer> timers = new LinkedBlockingQueue<>();     public static void processAsync(){         while(!scheduleMicrotasks.isEmpty() || !timers.isEmpty()){             Task task = null;             if((task = scheduleMicrotasks.poll()) != null){ }else if((task = timers.poll()) != null){}             task.run();         }     }     public static void main(String[] args){         System.out.println("main start!");         timers.offer(new Timer(){             @Override             public void run() {                 System.out.println("timer - event - A");                 scheduleMicrotasks.offer(new ScheduleMicrotask(){                     @Override                     public void run() {                         System.out.println("ScheduleMicrotask - A - in Timer A");                     }                 });                 scheduleMicrotasks.offer(new ScheduleMicrotask(){                     @Override                     public void run() {                         System.out.println("ScheduleMicrotask - B - in Timer A");                     }                 });             }         });         scheduleMicrotasks.offer(new ScheduleMicrotask(){             @Override             public void run() {                 System.out.println("ScheduleMicrotask - C - in MAIN ");                 timers.offer(new Timer(){                     @Override                     public void run() {                         System.out.println("timer - event - B - in ScheduleMicrotask - C ");                     }                 });             }         });        System.out.println("main end!");         processAsync();     } 
}

1.3.3 Dart实现方案

图例

说明

        一个Dart应用有一个消息循环和两个消息队列-- event队列和microtask队列。

        event队列包含所有外来的事件:I/O,mouse events,drawing events,timers,isolate之间的message等。

        microtask 队列在Dart中是必要的,因为有时候事件处理想要在稍后完成一些任务但又希望是在执行下一个事件消息之前。

        event队列包含Dart和来自系统其它位置的事件。但microtask队列只包含来自当前isolate的内部代码。

        正如下面的流程图,当main方法退出后,event循环就开始它的工作。首先它会以FIFO的顺序执行micro task,当所有micro task执行完后它会从event 队列中取事件并执行。如此反复,直到两个队列都为空。

        当一个Dart应用开始的标志是它的main isolate执行了main方法。当main方法退出后,main isolate的线程就会去逐一处理消息队列中的消息。

        正如下面的流程图,当main方法退出后,event循环就开始它的工作。首先它会以FIFO的顺序执行micro task,当所有micro task执行完后它会从event 队列中取事件并执行。如此反复,直到两个队列都为空。

        当一个Dart应用开始的标志是它的main isolate执行了main方法。当main方法退出后,main isolate的线程就会去逐一处理消息队列中的消息

实例

import 'dart:async'; 
/// 并行异步编程 
void main(){   print("main begin");   Timer.run(() {     print("timer - event - A");     scheduleMicrotask(() {       print("ScheduleMicrotask - A - in Timer A");     });     scheduleMicrotask(() {       print("ScheduleMicrotask - B - in Timer A");     });   });   scheduleMicrotask(() {     print("ScheduleMicrotask - C - in MAIN ");     Timer.run(() {       print("timer - event - B - in ScheduleMicrotask - C ");     });   });   print("main end"); 
}

2 Future 

2.1 概述

        Dart 的异步对象,类似于 Javascript 中的 Promise。

        Future 表示一个异步操作返回的结果;

        Future 是一个泛型类;

        Future 实现异步的原因是通过 dart 的 event loop,这里不过多赘述。

2.2 基本使用

2.2.1 说明

2.2.2 代码

import 'package:http/http.dart' as http; 
Future getIp() {   final url = 'https://httpbin.org/ip';   return http.get(url).then((res) {     return res.body;   }); 
} void main() {   getIp().then((res) {     print(res);   }).catchError((err) {     print(err);   }); 
}

2.3 方法

2.3.1 then

        通过 .then 的形式,获取 Future 对象执行成功后的结果。

示例

//第一个参数 onValue
void main() {   testThen().then((res) {     print(res); // 异步数据     // 可以将数据处理后,继续 return 出去,实现链式调用     return "加工后的${res}";   }).then((res) {     print(res); // 加工后的异步数据   }); 
}// 返回一个 Future<String> 对象,其中 String 可以省略,会被 dart 推断出来 
Future<String> testThen() {   // return 一个 Future 对象   return new Future(() {     // 发送给 .then 的数据     return "异步数据";   }); 
}//第二个参数 onError
void main() {   testThen().then((res) {     // 不执行 onValue 的方式     print(res);     return "加工后的${res}";   }, onError: (err) {     // 接收到 Future 的错误     print(err); // 这是一个异常   }); 
} Future<String> testThen() {   return new Future(() {     throw ('这是一个异常');   }); 
}

2.3.2 catchError

        它比 then/onError 的接受范围要广,它不仅能接受 Future 中的异常,还能接受 then 中的异常;

        如果没有填 then/onError

示例

void main() {   testThen().then((res) {       print(res);       return "加工后的${res}";     },onError: (err) {       print("onError: ${err}");       throw ("这是 onError  抛出的异常");     }).catchError((err) {       print("catchError: ${err}");     }); 
} Future<String> testThen() {   return new Future(() {     throw ('这是一个异常');   }); 
} 
// 执行结果 
// onError: 这是一个异常 
// catchError: 这是 onError  抛出的异常

2.3.3 whenComplete

        在 Future 完成之后总是会调用,不管是错误导致的完成还是正常执行完毕,并且返回一个 Future 对象。类似于 JavaScript 中的 Promise 中的 finally。

示例

void main() {   testThen().then((res) {     print(res);     return "加工后的${res}";   }, onError: (err) {     print("onError: ${err}");     throw ("这是 onError  抛出的异常");   }).catchError((err) {     print("catchError: ${err}");   }).whenComplete(() {     print("执行完成");   }); 
} Future<String> testThen() {   return new Future(() {     throw ('这是一个异常');   }); 
} 
// 执行结果 
// onError: 这是一个异常 
// catchError: 这是 onError  抛出的异常 
// 执行完成

2.4 静态方法

2.4.1 Future.delayed()

        执行一个延时任务。

示例

void main(List<String> args) {   print('start....');   Future.delayed(Duration(seconds: 3), () => print("延时任务"));   print('end....');       // 执行结果:   // start....   // end....   // 延时任务 
}//注: delayed 中实现的延时操作通过 Timer 来实现的,
//在实际开发中,如果只是一个单纯的延时操作,建议使用 Timer。void main(List<String> args) {print('start....');new Timer(Duration(seconds: 3), () => print("延时任务"));print('end....');// 执行结果:// start....// end....// 延时任务
}

2.4.2 Future.value()

        创建一个返回指定 value 值的 Future :

void main() {   print(1);   Future.value("abc").then((res) => print(res));   print(2);   // 执行结果   // 1   // 2     // abc 
}

2.5 执行顺序

2.5.1 说明

        future是一个异步任务的封装,提供了让自己进行任务转换的过程

2.5.2 方案

        Future() - 默认为timer

        Future.sync() - 同步在主线执行

        Future.microtask() - 转换微任务

        Future.value(val) 根据值选择,同时可以控制顺序

2.6 不常用,但是同样挺重要的方法

2.6.1 Future.wait()

        执行多个 Future,等待所有的 Future 执行完成。

void main() {   
Future.wait([futureA(), futureB()]).then((res) {     print(res);   }); 
} Future futureA() {   return Future.delayed(Duration(seconds: 3), () {     print("futureA 完成了");   }); 
} 
Future futureB() {   return Future.delayed(Duration(seconds: 5), () {     print("futureB 完成了");   }); 
} 
// 执行结果 
// futureA 完成了 
// futureB 完成了 
// [null, null]

2.6.2 Future.any()

        执行多个 Future,哪个先执行完成,就返回对应的数据。

void main() {   
Future.any([futureA(), futureB()]).then((res) {     // 此时 res 获取的是 futureA() 执行的结果     print(res);   }); 
} Future futureA() {   return Future.delayed(Duration(seconds: 3), () {     print("futureA 完成了");   }); 
} Future futureB() {   return Future.delayed(Duration(seconds: 5), () {     print("futureB 完成了");   }); 
} 
// 执行结果 
// futureA 完成了 
// null         -----------  Future.any 返回的结果 
// futureB 完成了

2.6.3 timeout

        设置异步操作的超时时长,返回的同样是个 Future.

import 'dart:async';void main() {futureA();
}Future futureA() {return Future(() {new Timer(Duration(seconds: 5), () {print("测试");});}).timeout(Duration(seconds: 3)).then((res) {// 设置超时print(res);print("超时后执行的 then");});
}
// 执行结果
// null
// 超时后执行的 then
// 测试

3 Stream  

3.1 简介

        Stream是一个异步的事件队列,也是响应式编程的实现

        编程和核心就是处理数据,从上游拿到数据,经过处理后传递给下游。

        后续随着需要处理的数据越来越多,有了集合的概念,包装了一系列数据在上下游之间进行传递。

        随着计算资源的发展,业务数据的处理可能要跨多个线程,这样数据处理流程就从单个线程的同步处理变成跨多个线程的异步处理。此时集合通常充当多个线程之间协作的缓冲池,当缓冲池满了的时候上游线程阻塞,空了的时候下游线程阻塞。

        在开发过程中经常需要开发人员写很多遍历数据集合的操作,就出现了集合的迭代器来简化遍历。在遍历过程中常会有筛选、排序、映射转换、合并或者拆分等操作。

        流的出现就是简化针对集合的这些操作,方便开发。流关注的是针对数据源的一系列操作而不是数据的存储。

        所以流就是一个数据源+管道(多个中间操作)+终端操作组成。

                数据源:定义流操作的原始数据是哪里来的,可以是集合、数组、生成器函数、io管道以及其他流作为数据来源。

                中间操作:针对流中的数据进行处理,处理产生的结果还是流:比如过滤filter、一对多flatMap、一对一的变换map、排序sorted等等操作。

                终端操作:定义了流中元素最终要经历的操作,比如最大最小值、个数统计、转换成集合或其他类型、提供对元素遍历的接口等。终端操作的执行意味着定义的流开始消耗,等最后一个元素经历过终端操作后,这个流就相当于完成了。

        流的中间操作是惰性的:中间操作并不会立即执行,而是等终端操作开始执行时才会被执行。

        中间操作又被分为

                (1)有状态的中间操作:比如排序、去重等操作,需要知道整个序列的所有元素才可以完成操作。

                (2)无状态的中间操作:比如过滤、映射等操作,单个元素就可以完成操作,不需要知道其他元素的状态。所以这种中间操作是可以并行处理的。

        短路操作:如果在提供无限流输入时,它可能会产生一个有限的流,那么他就是短路的。如果在无限流作为输入时,它可能在有限的时间内终止,这个终端操作是短路的。

        流不存储数据,流的中间操作并不修改数据源的结构。流的短路操作让其可以处理无线数据源。流的延迟特性让其可以优化中间操作。

        流的惰性让流可以处理异步事件。

        大致了解了流的特性之后,我们开始看Dart中的流是如何使用的。

        Future和Stream是Dart异步库中的核心,Future代表的是单个异步事件,Stream代表的是一个异步事件序列

3.2 创建流

3.2.1 通过生成器创建流

        async* 标记的方法称为异步生成器,在其中可以通过yield生成单个元素,yield*生成多个元素,最终汇集成流

示例

Stream<String> createStream() async* {   for (int i = 0; i < 100; i++) {     await Future.delayed(Duration(seconds: 1));     yield "第${i}份数据";   } 
} main() {   Stream<String> result = createStream();   Future future = result.forEach((element) {     print(element);   });   print('future开始!'); 
}

3.2.2 通过流命名构造函数创建流

        根据已有的数据序列创建流序列。

Stream.fromIterable(Iterable<T> elements)

        根据Future创建流

Stream.fromFuture(Future<T> future) Stream.fromFutures(Iterable<Future<T>> futures)

3.2.3 通过StreamController创建流

        其实上面(2)中创建流的方式底层都是通过StreamController来创建的。

StreamController<int> controller = StreamController(); 
Stream<int> stream = controller.stream;

3.3 流的订阅

3.3.1 说明

        Stream可以通过listen方法来创建流的订阅,需要回调函数。返回的是订阅类StreamSubscription;

        通订阅流可以监听流中的数据或事件。

3.3.2 单订阅流

        默认情况下创建的流都是单订阅流,单订阅流只能被订阅一次,第二次监听会报错!监听开始之前的元素不会被订阅。但 Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。

3.3.3 广播订阅

        广播流允许存在任意数量的 listener,并且无论是否存在 listener,它都能产生事件,所以中途加入的 listener 不会侦听到已发生的事件。

        单订阅流常用于数据的传递,广播流用于事件的分发。

3.4 常用方法

3.4.1 Sink

        Sink是流控制器最基础的接口,定义了一个通用的数据接收器所需要的方法,

3.4.2 EventSink

        EventSink是对Sink的扩展,添加了处理Error的方法。

3.4.3 StreamConsumer

        StreamConsumer 定义了可以接受流输入的槽

3.4.4 StreamSink

        StreamSink定义了可以同步或异步接收流事件的接口方法。

3.4.5 StreamController

        StreamController 是流控制器的核心接口,包含了流控制器该有的大多数接口方法。

        其中

                stream用于向外提供创建的Stream。

                sink 是该控制器关联的流的数据来源。可以使用sink.add 方法向流中添加数据。

                onListen, 当控制器中的流被监听的时候,会回调该方法。

                onPause, 当流的监听主动暂停的时候,会回调该方法。

                onResume, 当流的监听主动恢复监听的时候,会回调该方法。

                onCancel,当流的监听取消监听的时候,会回调该方法。

        流控制器中有两个工厂构造函数,分别构造了单订阅,和广播流以及同步和异步流。

3.5 案例

3.5.1 基本使用

import 'dart:async';/// stream 主要是为了实现消息通信方案
void main(){StreamController controller = StreamController();controller.stream.listen((event) {print("接受到数据:$event");});// //单一订阅模式下二次监听报错// controller.stream.listen((event) {//   print("接受到数据:$event");// });controller.sink.add("abc");controller.sink.add("123");
}

3.5.2 订阅模式-广播

import 'dart:async'; 
/// stream 主要是为了实现消息通信方案 
void main(){   StreamController controller = StreamController();   controller.stream.listen((event) {     print("接受到数据:$event");   });   // //单一订阅模式下二次监听报错   // controller.stream.listen((event) {   //   print("接受到数据:$event");   // });   controller.sink.add("abc");   controller.sink.add("123"); 
}

3.5.3 stream 创建方案


import 'dart:async';/// stream 创建方案
void main(){testFromFuture();// testFromFutures();// testFromIterable();// testPeriodic1();// testPeriodic2();
}Future<String> getData(){return Future.delayed(Duration(seconds: 5),(){return "当前时间:${DateTime.now()}";});
}void testFromFuture(){Stream.fromFuture(getData()).listen((event) {print("event:$event");}).onDone(() {print("done ");});
}void testFromFutures(){Stream.fromFutures([getData(),getData(),getData()]).listen((event) {print("event:$event");}).onDone(() {print("done ");});
}void testFromIterable(){var datas = [1,'hello',2,'he',5];Stream.fromIterable(datas).listen((event) {print("event:$event");}).onDone(() {print("done ");});
}void testPeriodic1(){Duration interval = Duration(seconds: 2);int i = 0;Stream.periodic(interval).listen((event) {print("event:$event");}).onDone(() {print("done ");});
}void testPeriodic2(){Duration interval = Duration(seconds: 2);int i = 0;//take(5) 指定最大提取Stream.periodic(interval,(data)=>data).listen((event) {print("event:$event");}).onDone(() {print("done ");});
}

3.5.4 API使用

import 'dart:async';/// stream 创建方案
void main() {// testTake();// testWhile();// testWhere();// testDistinct();// testSkip();testSkipWhile();
}void testTake() {Duration interval = Duration(seconds: 1);int i = 0;//take(5) 指定最大提取Stream.periodic(interval, (data) => data).take(5).listen((event) {print("event:$event");}).onDone(() {print("done ");});
}///循环
void testWhile() {Duration interval = Duration(seconds: 1);int i = 0;//take(5) 指定最大提取Stream.periodic(interval, (data) => data).takeWhile((element) {print("takeWhile element:$element");return element <= 5;}).listen((event) {print("event:$event");}).onDone(() {print("done ");});
}/// 条件过滤
void testWhere() {Duration interval = Duration(seconds: 2);int i = 0;//take(5) 指定最大提取Stream.periodic(interval, (data) => data).takeWhile((element) {return element <= 5;}).where((data) => data % 2 == 0).listen((event) {print("event:$event");}).onDone(() {print("done ");});
}/// 去重
void testDistinct() {var data = [1, 2, 'a', 'a', 1, 1, 3, 4];Stream.fromIterable(data).distinct().listen((event) {print("testDistinct listen:$event");}).onDone(() {print("testDistinct done");});
}void testSkip() {var data = [1, 2, 'a', 'a', 1, 1, 3, 4];Stream.fromIterable(data).skip(2).listen((event) {print("testDistinct listen:$event");}).onDone(() {print("testDistinct done");});
}void testSkipWhile() {Stream.periodic(Duration(seconds: 1),(data) => data).takeWhile((element) => element <= 6).skipWhile((element){return element <= 3;}).listen((event) {print("testDistinct listen:$event");}).onDone(() {print("testDistinct done");});
}

3.5.5 await-async

3.6 通信资源开辟问题

3.6.1 图例

相关文章:

【Flutter入门到进阶】Dart进阶篇---Dart异步编程

1 并行与并发的编程区别 1.1 并发与并行 1.1.1 说明 我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的并行车辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可以并行运行。 CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数…...

点云配准方法原理(NDT、ICP)

配准是点云处理中的一个基础问题&#xff0c;众多学者此问题进行了广泛而深入的研究&#xff0c;也出现了一系列优秀成熟的算法&#xff0c;在三维建模、自动驾驶等领域发挥着重要的作用。 本文主要介绍粗配准NDT (Normal Distribution Transform) 与 精配准ICP (Iterative Cl…...

大规模 IoT 边缘容器集群管理的几种架构-0-边缘容器及架构简介

&#x1f4da;️Reference: IoT 边缘计算系列文章 什么是边缘容器&#xff1f; 边缘容器的概念 边缘容器是分散的计算资源&#xff0c;尽可能靠近最终用户或设备&#xff0c;以减少延迟、节省带宽并增强整体数字体验。 可以访问互联网的设备数量每天都在增加。有包括但不限于…...

代码随想录算法训练营第45天动态规划 背包基础 1 2、 416. 分割等和子集

文章目录01背包基础 &#xff08;二维数组&#xff09;思路递推公式初始化遍历顺序一维dp数组&#xff08;滚动数组&#xff09;一维数组的递推公式遍历顺序LeetCode 416. 分割等和子集思路总结01背包基础 &#xff08;二维数组&#xff09; 思路 根据动态规划五部进行分析&a…...

QT学习记录(六)类对象属性

类对象属性用来描述类对象的一些信息和当前的状态。类对象属性可以由类的编写者在编写类的时候定义&#xff0c;也可以由类的使用者在使用对象的时候定义。 由类的编写者定义 QPROPERTY()宏就是用来定义一个对象属性。 以第二行属性举例 QPROPERTY(bool enabled READ isEnabl…...

Spring Cloud Alibaba从搭建到源码完整进阶教程

微服务简介 Spring Cloud Alibaba 微服务简介 Nacos注册中心配置中心 Spring Cloud Nacos实战&#xff08;一&#xff09;- 下载和安装 Spring Cloud Nacos实战&#xff08;二&#xff09;- 服务提供者注册 Spring Cloud Nacos实战&#xff08;三&#xff09;- 服务消费者…...

Spring Cloud Nacos实战(一)- 下载和安装

Spring Cloud Alibaba Nacos下载和安装 Nacos介绍 ​ Nacos&#xff08;Naming Configuration Service&#xff09; 是一个易于使用的动态服务发现、配置和服务管理平台&#xff0c;用于构建云原生应用程序 ​ 服务发现是微服务架构中的关键组件之一。Nacos 致力于帮助您发现…...

深入理解设备像素比

文章目录参考描述像素分辨率显示分辨率图像分辨率物理分辨率分辨率单位&#xff08;仅部分&#xff09;DPIPPI设备像素比设备物理像素设备独立像素设备像素比产生放大与缩小尾声参考 项目描述关于物理像素、逻辑像素&#xff08;css像素&#xff09;、分辨率、像素比的超详细讲…...

Revisiting Distributed Synchronous SGD 带有Back-up机制的分布式同步SGD方法 论文精读

论文链接&#xff1a;Revisiting Distributed Synchronous SGD ABS 本文介绍了用于分布式机器学习的同步和异步SGDSGDSGD&#xff0c;同时指出各自的缺点&#xff1a;stragglersstragglersstragglers和stalenessstalenessstaleness。 同时为了解决同步SGDSGDSGD存在straggle…...

shiro CVE-2020-13933

0x00 前言 同CVE-2020-1957&#xff0c;补充一下笔记&#xff0c;在CVE-2020-1957的基础上进行了绕过。 影响版本&#xff1a;Apache Shiro < 1.6.0 环境搭建参考&#xff1a;shiro CVE-2020-1957 0x01 漏洞复现 CVE-2020-13933中使用%3b绕过了shiro /*的检测方式&…...

斐波那契数列(递归+迭代)

目录什么是斐波那契数列递归写法使用递归写法的缺点迭代写法(效率高)什么是斐波那契数列 斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列&#xff0c;因数学家莱昂纳多斐波那契&#xff08;Leonardo Fibonacci&#xff09;以兔子繁殖为例…...

2022黑马Redis跟学笔记.实战篇(六)

2022黑马Redis跟学笔记.实战篇 六4.7.达人探店功能4.7.1.分享探店图文1. 达人探店-发布探店笔记2. 达人探店-查看探店笔记4.7.2.点赞功能4.7.3.基于List实现点赞用户列表TOP104.7.4.基于SortedSet实现点赞排行榜4.8.关注列表4.8.1.关注列表实现原理4.8.2.添加关注1. 好友关注-关…...

Linux-VMware常用设置(时间+网络)及网络连接激活失败解决方法-基础篇②

目录一、设置时间二、网络设置1. 激活网卡方法一&#xff1a;直接启动网卡&#xff08;仅限当此&#xff09;方法二&#xff1a;修改配置文件&#xff08;永久&#xff09;2. 将NAT模式改为桥接模式什么是是NAT模式&#xff1f;如何改为桥接模式&#xff1f;三、虚拟机网络连接…...

vue3学习总结1

一.vue3与vue2相比带来哪些变化&#xff1f;a.性能的提升&#xff08;包括打包大小减少&#xff0c;初次渲染的速度加快&#xff0c;更新渲染速度加快&#xff0c;内存减少&#xff09;b.源码的升级&#xff08;响应式的原理发生了变化&#xff0c;由原来的defineProperty变成了…...

SpringBoot统一功能处理

一、统一用户登录权限验证 1.1Spring拦截器 实现拦截器需要以下两步&#xff1a; 1.创建自定义拦截器&#xff0c;实现 HandlerInterceptor 接⼝的 preHandle&#xff08;执行具体方法之前的预处理&#xff09;方法。 2.将⾃定义拦截器加⼊ WebMvcConfigurer 的 addIntercept…...

2022年3月电子学会Python等级考试试卷(五级)答案解析

目录 一、单选题(共25题,共50分) 二、判断题(共10题,共20分) 三、编程题(共3题,共30分) 青少年软件编程(Python)等级考试试卷(五级&#...

【C++】智能指针

目录 一、先来看一下什么是智能指针 二、 auto_ptr 1、C98版本 2、C11的auto_ptr 三、boost 库中的智能指针 1. scoped_ptr 2、shared_ptr&#xff08;最好的智能指针&#xff09; 四、C11中新提供的智能指针 unique_ptr shared_ptr std::shared_ptr的循环引用问题…...

Seata架构篇 - AT模式

AT 模式 概述 Seata AT 模式是一种非侵入式的分布式事务解决方案&#xff0c;Seata 在内部做了对数据库操作的代理层&#xff0c;我们使用 Seata AT 模式时&#xff0c;实际上用的是 Seata 自带的数据源代理 DataSourceProxy&#xff0c;Seata 在这层代理中加入了很多逻辑&am…...

加油站会员管理小程序实战开发教程12

我们上一篇介绍了会员数据源的开发,本节我们介绍一下会员注册功能。 首先呢梳理一下会员注册的业务逻辑,如果用户是首次登录,那他肯定还没有给我们的小程序提交任何的信息。那么我们就在我的页面给他显示一个注册的按钮,如果他已经注册过了,那么就正常显示会员的信息,他…...

用腾讯云同步Obsidian笔记

介绍 之前用gitee同步OB笔记&#xff0c;同时做图床。但由于git系产品设置起来相对复杂&#xff0c;且后续可能有外链过审等问题。周五被同事小姐姐安利了用腾讯云COS&#xff0c;试了一下&#xff0c;果然不错。其主要优点如下&#xff1a; 设置简单&#xff0c;学习成本低&…...

变幻莫测:CoreData 中 Transformable 类型面面俱到(一)

概述 各位似秃似不秃小码农们都知道&#xff0c;在苹果众多开发平台中 CoreData 无疑是那个最简洁、拥有“官方认证”且最具兼容性的数据库框架。使用它可以让我们非常方便的搭建出 App 所需要的持久存储体系。 不过&#xff0c;大家是否知道在 CoreData 中还存在一个 Transfo…...

Python_day47

作业&#xff1a;对比不同卷积层热图可视化的结果 一、不同卷积层的特征特性 卷积层类型特征类型特征抽象程度对输入的依赖程度低层卷积层&#xff08;如第 1 - 3 层&#xff09;边缘、纹理、颜色、简单形状等基础特征低高&#xff0c;直接与输入像素关联中层卷积层&#xff08…...

React 中 HTML 插入的全场景实践与安全指南

在 React 开发过程中&#xff0c;我们常常会遇到需要插入 HTML 内容的场景。比如将服务端返回的富文本渲染到页面&#xff0c;还有处理复杂的 UI 结构&#xff0c;正确的 HTML 插入方式不仅影响页面展示效果&#xff0c;更关乎应用的安全性。 本文将详细探讨 React 中插入 HTM…...

Protobuf 中的类型查找规则

a.proto syntax "proto2"; //protoc3生成代码兼容proto2语法 package pkgA; message Example { }ba.proto package pkgB.pkgA; message Example { }b.proto syntax "proto3"; //protoc3生成代码兼容proto2语法 package pkgB; import "test1/a.pr…...

从零开始开发纯血鸿蒙应用之网络检测

从零开始开发纯血鸿蒙应用 〇、前言一、认识 connection 模块1、获取默认网络2、获取网络能力信息3、解析网络能力信息3.1、NetCap3.2、NetBearType 二、实现网络检测功能1、申请权限2、获取默认网路的 NetCap 数组 三、总结 〇、前言 在之前的博文里&#xff0c;介绍了如何实…...

kafka消息积压排查

kafka监控搭建&#xff1a;https://insights.blog.csdn.net/article/details/139129552?spm1001.2101.3001.6650.1&utm_mediumdistribute.pc_relevant.none-task-blog-2%7Edefault%7Ebaidujs_baidulandingword%7EPaidSort-1-139129552-blog-132216491.235%5Ev43%5Econtrol…...

抽奖系统核心——抽奖管理

目录 前端逻辑&#xff1a; 核心全局变量&#xff1a; reloadConf函数&#xff1a; nextStep函数&#xff1a; 后端实现&#xff1a; 抽奖接口: Controller层: Service层: MqReceiver&#xff1a; drawPrizeService&#xff1a; statusConvert()方法: activityStatu…...

如何使用Webhook触发器,在 ONLYOFFICE 协作空间构建智能工作流

在数字化办公中&#xff0c;ONLYOFFICE 协作空间作为一款功能强大的文档协作平台&#xff0c;提供了丰富的自动化功能。对于开发者而言&#xff0c;Webhook 触发器是实现业务流程自动化与系统集成的关键工具。本文将深入探讨如何在 ONLYOFFICE 协作空间中高效利用 Webhook&…...

【WPF】WPF 项目实战:用ObservableCollection构建一个可增删、排序的管理界面(含源码)

&#x1f4a1;WPF 项目实战&#xff1a;构建一个可增删、排序的光源类型管理界面&#xff08;含源码&#xff09; 在实际的图像处理项目中&#xff0c;我们经常需要对“光源类型”进行筛选或管理。今天我们来一步步构建一个实用的 WPF 界面&#xff0c;实现以下功能&#xff1…...

Java 大视界 -- 基于 Java 的大数据分布式计算在蛋白质组学数据分析中的加速与优化(255)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...