Dart 单线程模型的本质

众所周知 Dart​ 运行在单线程模型下,单线程模型在代码运行的过程中任意时刻只有一个线程参与(但整个周期内可能会有多个线程),意味着代码指令是顺序执行的也就不存在并发的情况。单线程模型的优点是实现简单、无资源竞争导致的异常,缺点是如果以 阻塞(Blocking)模式运行单线程模型的效率会非常低。为了提升单线程的运行效率 Dart​ 是以 非阻塞(Non-blocking)的模式运行在单线程模型中,因此 Dart​ 的单线程模型有着较高的效率,同时避免了资源竞争的问题。 阻塞(Blocking):当程序执行一个阻塞操作时,主线程会被挂起,直到该操作完成后才能继续执行后续的指令。在这期间,主线程无法执行其他的任务,因此程序会暂停执行。典型的阻塞操作包括文件I/O、网络I/O、等待用户输入等。在阻塞操作完成之前,主线程无法继续执行后续的指令。 非阻塞(Non-blocking):相比之下,当程序执行一个非阻塞操作时,主线程会立即返回,而不会被挂起等待操作完成。即使操作没有完成,主线程也可以继续执行后续的指令。 线程的阻塞、非阻塞通常与线程同步、异步对应。 阻塞:当程序执行一个阻塞任务时,当前线程会被挂起,只有在得到调用结果之后才会继续执行,这个过程称之为「同步调用」。 非阻塞:当程序执行一个阻塞任务时,当前线程不会停止执行,而是继续执行后面的其它任务,阻塞结束后再继续执行之前未完成的任务,这个过程称之为「异步调用」。 ​Dart​ 以非阻塞(Non-blocking)的单线程模型运行,自然也支持「同步」或「异步」调用。在 Dart​ 中一般用 Future​ 实例代表一个异步调用过程(也称为「Event Handler 宏任务」),每个 Future​ 异步任务具有相同的优先级且总是以先入先出(FIFO)的顺序执行。为了进一步提高响应实时性 Dart​ 提出了微任务(MicroTask)的概念,微任务优先级高于所有宏任务而微任务之间仍然以 FIFO 的顺序执行。假如某些宏任务需要满足一些前置条件,而微任务的存在保证了前置条件可以在宏任务执行前被设置,这进一步提高了Dart​单线程模型的实时性与灵活性。 ​Dart​ 非阻塞的单线程模型由 事件循环(EventLoop) 来实现,它包含两个上面提到的两个队列: 宏任务队列(Event Queue) 与 微任务队列(MicroTask) ;EventLoop​ 优先消费微任务队列(main 函数结束后立即消费微任务),待微任务完全消费完之后再消费宏任务队列。每个宏任务消费完毕之后都会去检查微任务队列是否为空,不为空则优先消费微任务队列。下图来描述上面的过程。 ​​ 实战 宏任务可以用 Future​ 对象创建,微任务则使用 scheduleMicrotask​ 全局函数来创建。可以通过一个例子来了解他们之前的优先级关系。 void main() async { print('m1'); var f = Future(() { print('f1'); scheduleMicrotask(() { print('fs1'); }); return Future....

五月 11, 2024 · 4 分钟 · LengYue

使用 Rust 开发 Android 底层库,并简化 Java 与 Rust 相互操作

前言 提到JNI,大家都会想到C,C++.不过如今Rust又给我们增加了一个选项,借助rust的jni库(https://github.com/jni-rs/jni-rs),我们可以很方便的使Android与rust交互.从本章起,我们将逐步地了解使用rust实现一些经典的jni方法. 关于 Rust 环境搭建、配置 Rust Android targets、linker,以及如何在 Android 上如何直接运行 Rust 代码,可以看上篇文章 将 Rust 编译为可在 Android 上使用的二进制文件 本文主要介绍如何使用 Rust 借助 J4RS 方便快捷的编写 Android Jni: 阅读本文,你需要具备、了解一下知识: Android 编写 JNI 的基本流程 Rust 代码基本的阅读能力 Android 开发的基本流程 Android 集成 Rust 配置 Rust Android Gradle Plugin Rust Android Gradle Plugin 这个 Gradle 插件的主要功能是帮你自动配置 Rust-Android 交叉编译,并将编译产物自动添加到 Android 项目。 这并不是必须的,你同样可以手动 build rust 项目。然后手动复制到 Android Studio 中使用。 因为我使用的是最新版本的 gradle , 采用 Kotlin kts 编写。在语法上稍有不同,旧版本写法可以在项目的 README 查看...

九月 7, 2023 · 3 分钟 · LengYue

将 Rust 编译为可在 Android 上使用的二进制文件

Rust 语言已经成为了越来越受欢迎的一种系统级编程语言。它被广泛使用来开发高性能的系统软件,模块化的库,以及并发和并行计算应用程序。不仅如此,它还可以为其他平台和设备生成二进制代码,包括 Android 操作系统。如果你也想在 Android 上利用 Rust 开发应用程序 创建Rust项目 首先创建 Rust 项目 cargo new rustDemo 为了跟前文(使用 GoMobile 创建 Android、iOS 跨平台 WebSocket Library)呼应,我们这里也使用 Rust 借助 tokio 写一个 WebSocket Server。 main.rs use async_trait::async_trait; use ezsockets::Error; use ezsockets::Server; use ezsockets::Socket; use std::net::SocketAddr; type SessionID = u16; type Session = ezsockets::Session<SessionID, ()>; struct EchoServer {} #[async_trait] impl ezsockets::ServerExt for EchoServer { type Session = EchoSession; type Call = (); async fn on_connect( &mut self, socket: Socket, address: SocketAddr, _args: (), ) -> Result<Session, Error> { let id = address....

三月 20, 2023 · 2 分钟 · LengYue

使用 GoMobile 创建 Android、iOS 跨平台 WebSocket Library

GoMobile 是 Go 语言的扩展,将 Go 代码编译为可在移动设备上运行的静态库或动态库,可在 iOS 和 Android 平台上使用。本文介绍如何使用 GoMobile 创建 Android 应用并在其中调用 Go 语言 WebSocket。 安装 GoMobile go install golang.org/x/mobile/cmd/gomobile@latest 创建 Go WebSocket 库 首先创建一个 Go WebSocket 服务端: mkdir go-websocket cd go-websocket go mod init go-socket // 使用 gomobile 初始 gomobile init 如果你无法使用 gomobile init 请检查 GOBIN 是否加入环境变量 我们使用在go 中广泛使用的gorilla/websocket来创建websocket服务端: go get github.com/gorilla/websocket 编写WebSocket服务端代码: package socket import ( "flag" "log" "net/http" "github....

三月 14, 2023 · 2 分钟 · LengYue

Flutter error not found dart html

场景:同一个项目,打包成 Flutter web 和 Flutter App。 问题:Error: Not found: ‘dart:html’。 产生上面问题的原因其实就是Flutter web使用了dart:html包的类,而Flutter App没有dart:html相关类。相应的 dart.io 这个包,在 web 中也是不存在。像类似的情况有很多。 要解决上面的问题也很简单。写一个类似 MVC 的调用文件就可以了。Flutter 开发中网络请求使用最多的应该就是 dio 了。我们就以 dio 为例子,写一个调用的实例: dio 初始化的时候需要指定HttpClientAdapter。在app 上为 DefaultHttpClientAdapter web 上为 BrowserHttpClientAdapter,如果全部用DefaultHttpClientAdapter web 端会提示不支持。如果这么写: idWeb? BrowserHttpClientAdapter() else DefaultHttpClientAdapter 就会出现跟上面一样的。 提示 package:dio/adapter_browser.dart not found。 解决: http_client_adapter:用于中间承接转化工具。 http_client_adapter_io.dart:如果是Flutter App就引入这个包 http_client_adapter_web.dart:如果是Flutter web就引入这个包 http_client_adapter: export './http_client_adapter_web.dart' if (dart.library.io) './http_client_adapter_io.dart'; http_client_adapter_io: import 'package:dio/adapter.dart'; import 'package:dio/dio.dart'; HttpClientAdapter getHttpClientAdapter() { return DefaultHttpClientAdapter(); } http_client_adapter_web...

四月 20, 2022 · 1 分钟 · LengYue

AndroidStudio新版本Logcat

我一直使用的是预览版的AndroidStudio,今天更新到2021.3.1 Canary 6,发现logcat变样子了。 现在是这个样子了。 嗯,就是这样子,外观到是好看多了,不过对于使用习惯的我们还是需要适应下,过滤还是可以的,就是你要显示自己应用时候不想之前那样可以选择了 需要你 使用正则过滤package:mine,当然你默认安装时候这里就会这样显示了,mine就是当前安装的应用,你也可以指定自己的包名的应用进行过滤。如果你想在增加其他过滤的表达式,增加一个空格在输入就可以,例如过滤Debug可以输入package:mine level:DEBUG 大家可以注意下左边的工具栏,方便我们配置查询日志信息。 一次往下说明下; 清空日志 新版本的Logcat你在日志面板右键没有clear了,只有close,所以点击这个按钮清空吧 自动滑动到日志面板的最新的日志记录位置 自动折行,就是日志一屏展示,超过的折行显示,其实这个感觉没啥用,我不喜欢折行看 第4个比较常用 如图,有几个选项,Standard View 就是默认的展示包含了所有的信息,时间,包名,类名,进程ID等 Compact View 这个模式比较不多,日志值显示时间,日志级别,具体信息 00:53:25.837 W maxLineHeight should not be -1. maxLines:1 lineCount:1 Custom View, 自己配置 5 左边剩下的大家都常用就不说了 希望可以帮助大家。。。。。

三月 22, 2022 · 1 分钟 · LengYue

Typecho to Hugo

这两天正式把博客从 typecho 转到 hugo 了。更准确的说应该是从动态博客转到了静态博客。 以前我是很抵制静态博客的。主要是感觉: 更新麻烦(现在仍然是这种感觉) 放在 github pages 上访问速度也不快,为了加速还需要套个 CDN。(同样是花钱,打折期间买个国内云服务器,速度不慢,还能干其他的) 配置真的很麻烦 这次换到 hugo 的起因是一个域名要备案,博客所在的域名要关闭评论系统,备案完后,打开评论发现,只要有评论的文章就卡死,然后 502。懒得折腾,直接关闭评论功能,发现关了后台也会卡死。索性转移到了 Hugo Hugo 如何安装配置网上很多这里不再赘述,主要说一下我是如何转换的。 部署在哪里 本来打算部署在腾讯对象存储 COS 上的,不过最后还是放弃了。 域名备案,内容审查。 不方便自动化处理,虽然通过github action + 云函数 + hook 可以做到更新仓库后自动构建,下载到 COS 但还是略显麻烦。需要配置好几个地方。 最终选择了部署在 https://vercel.com 国内访问速度开可以。可以关联 github 仓库,不需要自动设置,可以自定义域名,似乎能自动配置 SSL 证书(表现为我设置了域名后访问自动变成 https ,证书是 Let's Encrypt 的免费证书)最后来个测速。 文章迁移 typecho 有个插件,能把文章导出为 markdown 格式,但是导出的文章并不符合 hugo 的要求,hugo 在文章前面可以加很多参数的比如 --- title: "Typecho to Hugo" slug: "typecho-to-hugo" date: 2022-03-11T10:49:59+08:00 categories: [岁月如歌] tags: [Typecho,Hugo] showToc: true TocOpen: true draft: false # description: "Desc Text....

三月 11, 2022 · 1 分钟 · LengYue

GetX Router 设置返回值

通过别名导航: var result = await Get.toNamed(Routes.WEB_VIEW, arguments: { "url": item?.link ?? "", "index": index, "collect": item?.collect ?? false, }); 返回值: Get.back(result: {"collect": collect.value}); 完整代码: () async { /// 导航到新的界面 var result = await Get.toNamed(Routes.WEB_VIEW, arguments: { "url": item?.link ?? "", "index": index, "collect": item?.collect ?? false, }); /// 接收返回值 bool collect = result["collect"]; }

二月 18, 2022 · 1 分钟 · LengYue