cangjie仓颉编程语言学习Note-2.标准库学习

cangjie仓颉编程语言学习Note-2.标准库学习

当前仓颉标准库提供了几乎涵盖常见开发所使用的常见库,这一点很赞!
详细模块如下:

  • std: 意指标准库,标准库是指在编程语言中预先定义的一组函数、类、结构体等,旨在提供常用的功能和工具,以便开发者能够更快速、更高效地编写程序。
    • core 包是标准库的核心包,提供了适用仓颉语言编程最基本的一些 API 能力。
    • argopt 包提供从命令行参数字符串解析出参数名和参数值的相关能力。
    • ast 包主要包含了仓颉源码的语法解析器和仓颉语法树节点,提供语法解析函数。
    • binary 包提供了基础数据类型和二进制字节数组的不同端序转换接口,以及端序反转接口。
    • collection 包提供了常见数据结构的高效实现、相关抽象的接口的定义以及在集合类型中常用的函数功能。
    • collection.concurrent 包提供了并发安全的集合类型实现。
    • console 包提供和标准输入、标准输出、标准错误进行交互的方法。
    • convert 包提供从字符串转到特定类型的 Convert 系列函数。
    • crypto.cipher 包提供对称加解密通用接口。
    • crypto.digest 包提供常用摘要算法的通用接口,包括 MD5、SHA1、SHA224、SHA256、SHA384、SHA512、HMAC、SM3。
    • database.sql 包提供仓颉访问数据库的接口。
    • deriving 包提供一组宏来自动生成接口实现。
    • ffi.python 包提供仓颉与 Python 语言互操作调用的能力,以兼容强大的计算和 AI 生态。
    • format 包提供格式化能力,主要为将仓颉类型实例转换为格式化字符串。
    • fs(file system)包提供对文件、文件夹、路径、文件元数据信息的一些操作函数。
    • io 包提供程序与外部设备进行数据交换的能力。
    • log 包提供日志管理和打印功能。(已废弃,请使用 log 包)
    • math 包提供常见的数学运算,常数定义,浮点数处理等功能。
    • math.numeric 包对基础类型可表达范围之外提供扩展能力。
    • net 包提供常见的网络通信功能。
    • objectpool 包提供了对象缓存和复用的功能。
    • posix 包主要适配 POSIX 系统接口。
    • process 包主要提供 Process 进程操作接口,主要包括进程创建,标准流获取,进程等待,进程信息查询等。
    • overflow 包提供了溢出处理相关能力。
    • random 包提供生成伪随机数的能力。
    • reflect 包提供了反射功能,使得程序在运行时能够获取到各种实例的类型信息,并进行各种读写和调用操作。
    • regex 包使用正则表达式分析处理文本的能力(仅支持 Ascii 编码字符串),支持查找、分割、替换、验证等功能。
    • runtime 包的作用是与程序的运行时环境进行交互,提供了一系列函数和变量,用于控制、管理和监视程序的执行。
    • socket 包用于进行网络通信,提供启动 Socket 服务器、连接 Socket 服务器、发送数据、接收数据等功能。
    • sort 包提供数组类型的排序函数。
    • sync 包提供并发编程相关的能力。
    • time 包提供了与时间相关的类型,包括日期时间,时间间隔,单调时间和时区等,并提供了计算和比较的功能。
    • unicode 包提供了按 unicode 编码标准处理字符的能力。
    • unittest 包用于编写仓颉项目单元测试代码,提供包括代码编写、运行和调测在内的基本功能。
  • compress: 模块提供压缩解压功能。
    • zlib 包提供压缩解压能力。
  • crypto: 模块提供安全加密能力。
    • crypto 包提供安全随机数功能。
    • digest 包提供常用的消息摘要算法,包括 MD5、SHA1、SHA224、SHA256、SHA384、SHA512、HMAC、SM3等。
    • keys 包提供非对称加密和签名算法,包括 RSA 和 SM2 非对称加密算法以及 ECDSA 签名算法。
    • x509 包提供处理数字证书功能,提供包括解析和序列化 X509 证书、验证证书、创建自签名证书、创建和验证证书链等主要功能。
  • encoding: 模块提供字符编解码功能。
    • base 包提供字符串的 Base64 编码及解码。
    • hex 包提供字符串的 Hex 编码及解码。
    • json 包用于对 json 数据的处理,实现 String, JsonValue, DataModel 之间的相互转换。
    • json.stream 包主要用于仓颉对象和 JSON 数据流之间的互相转换。
    • url 包提供了 URL 相关的能力,包括解析 URL 的各个组件,对 URL 进行编解码,合并 URL 或路径等。
  • fuzz: 模块提供基于覆盖率反馈的模糊测试能力。
    • fuzz 包为开发者提供基于覆盖率反馈的仓颉 fuzz 引擎及对应的接口,开发者可以编写代码对 API 进行测试。
  • log: 模块提供了日志记录相关的能力。
    • log 包提供了一个单一的日志API,它抽象了实际的日志实现。
  • logger: 模块提供文本格式和 JSON 格式日志打印功能。
    • logger 包提供文本格式和 JSON 格式日志打印功能。
  • net: 模块提供了网络通信相关的能力。
    • http 包提供 HTTP/1.1,HTTP/2,WebSocket 协议的 server、client 端实现。
    • tls 包用于进行安全加密的网络通信,提供创建 TLS 服务器、基于协议进行 TLS 握手、收发加密数据、恢复 TLS 会话等能力。
  • serialization: 模块提供了序列化和反序列化能力。
    • serialization 包提供了序列化和反序列化的能力。

下面我们以专题方式,串联所有模块。

常见数据结构

Array/List/Set/Map

常用的几种基础 Collection 类型,包含 Array、ArrayList、HashSet、HashMap。他们都是引用类型

  • Array:如果我们不需要增加和删除元素,但需要修改元素,就应该使用它。
  • ArrayList:如果我们需要频繁对元素增删查改,就应该使用它。
  • HashSet:如果我们希望每个元素都是唯一的,就应该使用它。
  • HashMap:如果我们希望存储一系列的映射关系,就应该使用它。
import std.collection.*main() {let list = ArrayList<Int64>([0, 1, 2])for (i in list) {println("The element is ${i}")}let a1 = list[0] // a == 0let a2 = list[1] // b == 1let a3 = list[-1] // Runtime exceptionslist[0] = 3  // oklet b = ArrayList<String>(100) // size = 100let c = ArrayList<String>(2, {x: Int64 => x.toString()})  // size = 2,  init with x
}// HashSet
let mySet = HashSet<Int64>([0, 1, 2])
let a = mySet.contains(0) // a == true
let b = mySet.contains(-1) // b == false
mySet.put(0)
mySet.remove(1) // HashMap
import std.collection.*main() {let map = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])for ((k, v) in map) {println("The key is ${k}, the value is ${v}")}let b = map["b"] // b == 1let c = map["d"] // Runtime exceptionsmap.contains("a") // truemap["a"] = 3 // okmap.put("a", 0) // okmap.remove("d")
}

Range、Array、ArrayList 其实都是通过 Iterable 来支持 for-in 语法的。

interface Iterable<T> {func iterator(): Iterator<T>...
}interface Iterator<T> <: Iterable<T> {mut func next(): Option<T>...
}// 所以
let list = [1, 2, 3]
for (i in list) {println(i)
}// 它等价如下代码
var it = list.iterator()
while (true) {match (it.next()) {case Some(i) => println(i)case None => break}
}
// or
while (let Some(i) <- it.next()) {println(i)
}

Exception/Error

在仓颉中,异常类有 ErrorException

  • Error 类描述仓颉语言运行时,系统内部错误和资源耗尽错误,应用程序不应该抛出这种类型错误,如果出现内部错误,只能通知给用户,尽量安全终止程序。
  • Exception 类描述的是程序运行时的逻辑错误或者 IO 错误导致的异常,例如数组越界或者试图打开一个不存在的文件等,这类异常需要在程序中捕获处理。

用户不可以通过继承仓颉语言内置的 Error 或其子类类来自定义异常,但是可以继承内置的 Exception 或其子类来自定义异常

open class FatherException <: Exception {public init() {super("This is FatherException.")}public open override func getClassName(): String {"FatherException"}
}class ChildException <: FatherException {public init() {super("This is ChildException.")}public open override func getClassName(): String {"ChildException"}
}

下面列表展示了 Exception 的主要函数及其说明:

函数种类函数及说明
构造函数init() 默认构造函数。
构造函数init(message: String) 可以设置异常消息的构造函数。
成员属性open prop message: String 返回发生异常的详细信息。该消息在异常类构造函数中初始化,默认空字符串。
成员函数open func toString(): String 返回异常类型名以及异常的详细信息,其中,异常的详细信息会默认调用 message。
成员函数func getClassName(): String 返回用户定义的类名,子类需要重写该方法以返回子类的名称。
成员函数func printStackTrace(): Unit 打印堆栈信息至标准错误流。

下面列表展示了 Error 的主要函数及其说明:

函数种类函数及说明
成员属性open prop message: String 返回发生错误的详细信息。该消息在错误发生时,内部初始化,默认空字符串。
成员函数open func toString(): String 返回错误类型名以及错误的详细信息,其中,错误的详细信息会默认调用 message。
成员函数func getClassName(): String 返回用户定义的类名,子类需要重写该方法以返回子类的名称。
成员函数func printStackTrace(): Unit 打印堆栈信息至标准错误流。

异常的捕获

// 普通方式
main() {try {throw NegativeArraySizeException("I am an Exception!")} catch (e: OverflowException) {println(e.message)println("OverflowException is caught!")} catch (e: IllegalArgumentException | NegativeArraySizeException) {println(e.message)println("IllegalArgumentException or NegativeArraySizeException is caught!")} catch ( _ ) {   // 它可以捕获所有异常println("catch an exception!")} finally {println("finally is executed!")}println("This will also be printed!")
}// try-with-resource
// 对象必须实现 Resource 接口才可以使用
class R <: Resource {public func isClosed(): Bool {true}public func close(): Unit {print("R is closed")}
}main() {try (r = R()) {println("Get the resource")let r = 0 // Error, redefinitionprintln(r)}
}

Option

Option 是一种非常常用的类型,所以仓颉为其提供了多种解构方式,具体包括:模式匹配、getOrThrow 函数、coalescing 操作符(??),以及问号操作符(?)。

  1. 模式匹配 match
func getString(p: ?Int64): String{match (p) {case Some(x) => "${x}"case None => "none"}
}
main() {let a = Some(1)let b: ?Int64 = None  // ? 等效于 Option<T>let r1 = getString(a)let r2 = getString(b)println(r1) // 1println(r2) // none
}
  1. coalescing 即 ??
main() {let a = Some(1)let b: ?Int64 = Nonelet r1: Int64 = a ?? 0  // 如果 a == None, 则返回 0. 否则返回 a 的 Some(v)中的 v .let r2: Int64 = b ?? 0  // 一样的println(r1) // 1println(r2) // 0
}
  1. ?. 操作符,和?类型不一样. 用以实现 Option 类型对 .()[]{} 的支持
// 结构体支持
struct R {public var a: Int64public init(a: Int64) {this.a = a}
}let r = R(100)
let x = Some(r)
let y = Option<R>.None
let r1 = x?.a   // r1 = Option<Int64>.Some(100)
let r2 = y?.a   // r2 = Option<Int64>.None// 对class的支持
struct A {let b: B = B()
}struct B {let c: Option<C> = C()let c1: Option<C> = Option<C>.None
}struct C {let d: Int64 = 100
}let a = Some(A())
let a1 = a?.b.c?.d // a1 = Option<Int64>.Some(100)
let a2 = a?.b.c1?.d // a2 = Option<Int64>.None
  1. getOrThrow 支持
main() {let a = Some(1)let b: ?Int64 = Nonelet r1 = a.getOrThrow()  // Option 转 Exceptionprintln(r1)try {let r2 = b.getOrThrow()} catch (e: NoneValueException) {println("b is None")}
}

IO 操作

仓颉编程语言将标准输入输出、文件操作、网络数据流、字符串流、加密流、压缩流等等形式的操作,统一用 Stream 描述。Stream 主要面向处理原始二进制数据,Stream 中最小的数据单元是 Byte。仓颉编程语言将 Stream 定义成了 interface,它让不同的 Stream 可以用装饰器模式进行组合,极大地提升了可扩展性。

仓颉编程语言中常见的节点流包含标准流(StdIn、StdOut、StdErr)、文件流(File)、网络流(Socket)等。

标准流包含了标准输入流(stdin)、标准输出流(stdout)和标准错误输出流(stderr)。使用 Console 类型来分别访问它们.

import std.console.*main() {// inlet txt = Console.stdIn.readln()println(txt ?? "")// outfor (i in 0..1000) {Console.stdOut.writeln("hello, world!")}Console.stdOut.flush()
}// 文件操作
import std.fs.*main() {let exist = exists("./tempFile.txt")println("exist: ${exist}")copy("./tempFile.txt", to: "./tempFile2.txt", overwrite: false)rename("./tempFile2.txt",  to: "./tempFile3.txt", overwrite: false)remove("./tempFile3.txt")let bytes = File.readFrom("./tempFile.txt") // 一次性读取了所有的数据File.writeTo("./otherFile.txt", bytes) // 把数据一次性写入另一个文件中
}// 文件也是流
// public class File <: Resource & IOStream & Seekable
internal import std.fs.*
internal import std.io.*main() {let file = File.create("./tempFile.txt")file.write("hello, world!".toArray())// 打开let file2 = File.openRead("./tempFile.txt")let bytes = readToEnd(file2) // 读取所有数据println(bytes)// trytry (file3 = File.openRead("./tempFile.txt")) {...// 结束使用后自动释放文件}
}

仓颉编程语言中常见的处理流包含 BufferedInputStreamBufferedOutputStreamStringReaderStringWriterChainedInputStream 等。

import std.io.*main(): Unit {let arr1 = "012\n346789".toArray()let byteBuffer = ByteBuffer()byteBuffer.write(arr1)let stringReader = StringReader(byteBuffer)/* 读取一行数据 */let line = stringReader.readln()println(line ?? "error") // 012
}main(): Unit {let byteBuffer = ByteBuffer()let stringWriter = StringWriter(byteBuffer)/* 写入字符串 */stringWriter.write("number")/* 写入字符串并自动转行 */stringWriter.writeln(" is:")/* 写入数字 */stringWriter.write(100.0f32)stringWriter.flush()println(String.fromUtf8(readToEnd(byteBuffer))) // number is:\n100.000000
}

Socket

在仓颉标准库中,用户可使用 std 模块下的 socket 包来实现传输层网络通信。
在传输层协议中,分为不可靠传输和可靠传输两种,仓颉将其抽象为 DatagramSocket 和 StreamSocket。其中不可靠传输协议常见的是 UDP,可靠传输协议常见的是 TCP,仓颉分别将其抽象为 UdpSocket 和 TcpSocket。另外,仓颉也实现了对传输层 Unix Domain 协议的支持,并支持其通过可靠和不可靠传输两种方式进行通信。
在应用层协议中,较为常见的是 HTTP 协议, 仓颉目前支持 HTTP/1.1、HTTP/2.0 等, 并支持从 HTTP 协议升级至 WebSocket 协议。

需要注意的是,仓颉的网络编程是阻塞式的。但被阻塞的是仓颉线程,阻塞中的仓颉线程会将系统线程让渡出去,因此并不会真正阻塞一个系统线程。

Tcp Server and client

package cjdemoimport std.socket.*
import std.time.*
import std.sync.*
import std.net.*var SERVER_PORT: UInt16 = 0func runTcpServer() {try (serverSocket = TcpServerSocket(bindAt: SERVER_PORT)) {serverSocket.bind()SERVER_PORT = (serverSocket.localAddress as IPSocketAddress)?.port ?? 0println("server port: ${SERVER_PORT}")try (client = serverSocket.accept()) {println("client: ${client.localAddress} ${client.remoteAddress}")let buf = Array<Byte>(10, repeat: 0)let count = client.read(buf)// 服务端读取到的数据为: [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]println("Server read ${count} bytes: ${buf}") // count = 3}}
}main(): Int64 {// 线程中执行tcp serverlet future = spawn {runTcpServer()}sleep(Duration.millisecond * 500)// tcp clienttry (socket = TcpSocket("127.0.0.1", SERVER_PORT)) {socket.connect()socket.write([1, 2, 3])}future.get()return 0
}

Http Server + websocket

注意:cangjie的 http/websocket 依赖 OpenSSL的lib和dll.
需要参考 https://docs.cangjie-lang.cn/docs/0.53.13/libs/net/http/http_package_overview.html 手动将openssl环境配好。

package cjdemoimport net.http.*
import encoding.url.*
import std.time.*
import std.sync.*
import std.collection.*
import log.*// 1. 构建 Http Server 实例
let server = ServerBuilder().addr("127.0.0.1").port(0).build()// http server websocket handler
func ws_handler(ctx: HttpContext): Unit {// 完成 websocket 握手,获取 websocket 实例let websocketServer = WebSocket.upgradeFromServer(ctx)// 读取发送过来的frame到datalet data = ArrayList<UInt8>()var frame = websocketServer.read()while(true) {match(frame.frameType) {case ContinuationWebFrame =>data.appendAll(frame.payload)if (frame.fin) {break}case TextWebFrame | BinaryWebFrame =>if (!data.isEmpty()) {throw Exception("invalid frame")}data.appendAll(frame.payload)if (frame.fin) {break}case CloseWebFrame =>websocketServer.write(CloseWebFrame, frame.payload)breakcase PingWebFrame =>websocketServer.writePongFrame(frame.payload)case _ => ()}frame = websocketServer.read()}println("server data: ${String.fromUtf8(data.toArray())}")    // hello// 发送数据// 发 256 个 awebsocketServer.write(TextWebFrame, Array<UInt8>(256, { _ => 97 }))// 收发 CloseFrame (由client 发起)let websocketFrame = websocketServer.read()println("server close frame type: ${websocketFrame.frameType}")   // CloseWebFrameprintln("server close frame payload: ${websocketFrame.payload}")     // 3, 232websocketServer.write(CloseWebFrame, websocketFrame.payload)// 关闭底层连接websocketServer.closeConn()
}func startServer(): Unit {// 2. 注册请求处理逻辑server.distributor.register("/hello", {httpContext =>httpContext.responseBuilder.body("Hello Cangjie!")})server.distributor.register("/ws", ws_handler)server.logger.level = LogLevel.OFF// 3. 启动服务server.serve()
}// 客户端复用 http client进行websocket连接
func client_ws(c:Client) {let url = URL.parse("ws://127.0.0.1:${server.port}/ws")let (ws, headers) = WebSocket.upgradeFromClient(c, url)for( (k,v) in  headers) {println("ws header => ${k} : ${v.toArray()}")}println()// 发送 hellows.write(TextWebFrame, "hello from client".toArray())// 接受消息let data = ArrayList<UInt8>()var frame = ws.read()while(true) {match(frame.frameType) {case ContinuationWebFrame =>data.appendAll(frame.payload)if (frame.fin) {break}case TextWebFrame | BinaryWebFrame =>if (!data.isEmpty()) {throw Exception("invalid frame")}data.appendAll(frame.payload)if (frame.fin) {break}case CloseWebFrame =>ws.write(CloseWebFrame, frame.payload)breakcase PingWebFrame =>ws.writePongFrame(frame.payload)case _ => ()}frame = ws.read()}println("client recv data size: ${data.size}")      // 4097println("client recv: ${String.fromUtf8(data.toArray())}")        // a// 收发 CloseFramews.writeCloseFrame(status: 1000)let websocketFrame = ws.read()println("client close frame type: ${websocketFrame.frameType}")      // CloseWebFrameprintln("client close frame payload: ${websocketFrame.payload}")     // 3, 232// 关闭底层连接ws.closeConn()//server.close()
}func startClient(): Unit {// 1. 构建 client 实例let client = ClientBuilder().build()// 2. 发送 requestlet response = client.get("http://127.0.0.1:${server.port}/hello")// 3. 读取response bodylet buffer = Array<Byte>(1024, repeat: 0);for( (k,v) in response.headers ) {println("header => ${k} : ${v.toArray()}")}println()let length = response.body.read(buffer)println(String.fromUtf8(buffer[..length]))// 请求 websocketprintln("\r\nconnect to ws....")client_ws(client)// 4. 关闭连接client.close()
}main () {spawn {startServer()}sleep(Duration.second)println("http server liston on: ${server.addr} ${server.port}")startClient()
}

输出

http server liston on: 127.0.0.1 53610
header => connection : [keep-alive]
header => date : [Fri, 01 Nov 2024 06:29:34 GMT]
header => content-length : [14]Hello Cangjie!connect to ws....
ws header => upgrade : [websocket]
ws header => connection : [upgrade]
ws header => sec-websocket-accept : [wsfGZnzlkFVlbBYKIcUZyqVvZqs=]
ws header => date : [Fri, 01 Nov 2024 06:29:34 GMT]
ws header => content-length : [0]server data: hello from client
client recv data size: 256
client recv: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
server close frame type: CloseWebFrame
server close frame payload: [3, 232]
client close frame type: CloseWebFrame
client close frame payload: [3, 232]

并发

仓颉编程语言提供抢占式的线程模型作为并发编程机制, 可以细化为两种不同概念,语言线程和 native 线程。

允许多个语言线程在多个 native 线程上切换执行,这种也被称为 M:N 线程模型,即 M 个语言线程在 N 个 native 线程上调度执行,其中 M 和 N 不一定相等。
当前,仓颉语言的实现同样采用 M:N 线程模型;因此,仓颉线程本质上是一种用户态的轻量级线程,支持抢占且相比操作系统线程更轻量化。

import std.sync.*
import std.time.*main(): Int64 {spawn { =>println("New thread before sleeping")sleep(100 * Duration.millisecond) // sleep for 100ms.println("New thread after sleeping")println("Current thread id: ${Thread.currentThread.id}")}// fut.get() // wait for the thread to finish.println("Main thread")return 0
}

在上面的例子中,新创建的线程会由于主线程结束而提前结束,在缺乏顺序保证的情况下,甚至可能会出现新创建的线程还来不及得到执行就退出了。我们可以通过 spawn 表达式的返回值,来等待线程执行结束。

spawn 表达式的返回类型是 Future<T>,其中 T 是类型变元,其类型与 lambda 表达式的返回类型一致。当我们调用 Future<T>get() 成员函数时,它将等待它的线程执行完成。

终止一个线程

可以通过 Future<T>cancel() 方法向对应的线程发送终止请求,该方法不会停止线程执行。开发者需要使用 ThreadhasPendingCancellation 属性来检查线程是否存在终止请求。

import std.sync.SyncCountermain(): Unit {let syncCounter = SyncCounter(1)let fut = spawn {syncCounter.waitUntilZero()// Check cancellation requestif (Thread.currentThread.hasPendingCancellation) {println("cancelled")return}println("hello")}fut.cancel()    // Send cancellation requestsyncCounter.dec()fut.get() // Join thread
}

线程同步

仓颉编程语言提供三种常见的同步机制来确保数据的线程安全:原子操作、互斥锁和条件变量。

  • 原子操作 Atomic, 提供整数类型、Bool 类型和引用类型的原子操作
    • loade/store/swap/compareAndSwap/fetchAdd/fetchSub/fetchAnd/fetchOr/fetchXor
    • 类型 AtomicInt8/16/32/64 ...
import std.sync.*
import std.time.*
import std.collection.*let count = AtomicInt64(0)main(): Int64 {let list = ArrayList<Future<Int64>>()// create 1000 threads.for (i in 0..1000) {let fut = spawn {sleep(Duration.millisecond) // sleep for 1ms.count.fetchAdd(1)}list.append(fut)}// Wait for all threads finished.for (f in list) {f.get()}let val = count.load()println("count = ${val}")return 0
}
  • 可重入互斥锁 ReentrantMutex, 对临界区加以保护,使得任意时刻最多只有一个线程能够执行临界区的代码
    1. 在访问共享数据之前,必须尝试获取锁;
    2. 处理完共享数据后,必须进行解锁,以便其他线程可以获得锁。
import std.sync.*
import std.time.*
import std.collection.*var count: Int64 = 0
let mtx = ReentrantMutex()main(): Int64 {let list = ArrayList<Future<Unit>>()// creat 1000 threads.for (i in 0..1000) {let fut = spawn {sleep(Duration.millisecond) // sleep for 1ms.mtx.lock()count++mtx.unlock()}list.append(fut)}// Wait for all threads finished.for (f in list) {f.get()}println("count = ${count}")return 0
}
  • Monitor 是一个内置的数据结构,它绑定了互斥锁和单个与之相关的条件变量(也就是等待队列)。
import std.sync.*
import std.time.*var mon = Monitor()
var flag: Bool = truemain(): Int64 {let fut = spawn {mon.lock()while (flag) {println("New thread: before wait")mon.wait()println("New thread: after wait")}mon.unlock()}// Sleep for 10ms, to make sure the new thread can be executed.sleep(10 * Duration.millisecond)mon.lock()println("Main thread: set flag")flag = falsemon.unlock()mon.lock()println("Main thread: notify")mon.notifyAll()mon.unlock()// wait for the new thread finished.fut.get()return 0
}
  • MultiConditionMonitor 是一个内置的数据结构,它绑定了互斥锁和一组与之相关的动态创建的条件变量。该类应仅当在 Monitor 类不足以满足复杂的线程间同步的场景下使用。

  • 仓颉编程语言提供一个 synchronized 关键字,搭配 ReentrantMutex 一起使用,可以在其后跟随的作用域内自动进行加锁解锁操作,用来解决类似的问题。

import std.sync.*
import std.time.*
import std.collection.*var count: Int64 = 0
let mtx = ReentrantMutex()main(): Int64 {let list = ArrayList<Future<Unit>>()// creat 1000 threads.for (i in 0..1000) {let fut = spawn {sleep(Duration.millisecond) // sleep for 1ms.// Use synchronized(mtx), instead of mtx.lock() and mtx.unlock().synchronized(mtx) {count++}}list.append(fut)}// Wait for all threads finished.for (f in list) {f.get()}println("count = ${count}")return 0
}
  • 线程局部变量 ThreadLocal

main(): Int64 {let tl = ThreadLocal<Int64>()let fut1 = spawn {tl.set(123)println("tl in spawn1 = ${tl.get().getOrThrow()}")  // 123}let fut2 = spawn {tl.set(456)println("tl in spawn2 = ${tl.get().getOrThrow()}")  // 456}fut1.get()fut2.get()0
}

package

在仓颉编程语言中,包是编译的最小单元,每个包可以单独输出 AST 文件、静态库文件、动态库文件等产物。每个包有自己的名字空间,在同一个包内不允许有同名的顶层定义或声明(函数重载除外)。

模块是若干包的集合,是第三方开发者发布的最小单元。一个模块的程序入口只能在其根目录下,它的顶层最多只能有一个作为程序入口的 main ,该 main 没有参数或参数类型为 Array<String>,返回类型为整数类型或 Unit 类型。

声明

// file 1
// Comments are accepted
package test
// declarations...// file 2
let a = 1 // Error, package declaration must appear first in a file
package test
// declarations...

导入

package a
import std.math.*
import package1.foo
import {package1.foo, package2.bar}// 重命名
import p1 as A
import p1 as B

诸如 StringRange 等类型能直接使用,并不是因为这些类型是内置类型,而是因为编译器会自动为源码隐式的导入 core 包中所有的 public 修饰的声明。

宏可以理解为一种特殊的函数。一般的函数在输入的值上进行计算,然后输出一个新的值,而宏的输入和输出都是程序本身。

let x = 3
let y = 2
@dprint(x)        // 打印 "x = 3"
@dprint(x + y)    // 打印 "x + y = 5"// dprint 的实现
macro package defineimport std.ast.*public macro dprint(input: Tokens): Tokens {let inputStr = input.toString()let result = quote(print($(inputStr) + " = ")println($(input)))return result
}

仓颉提供了几个内置编译标记,用于在编译时获取源代码的位置。

  • @sourcePackage() 展开后是一个 String 类型的字面量,内容为当前宏所在的源码的包名
  • @sourceFile() 展开后是一个 String 类型的字面量,内容为当前宏所在的源码的文件名
  • @sourceLine() 展开后是一个 Int64 类型的字面量,内容为当前宏所在的源码的代码行

内置编译标记 @When 来完成条件编译,编译条件使用 [] 括起来,[] 内支持输入一组或多组编译条件。@When 可以作用于导入节点和除 package 外的声明节点。

@When[os == "Linux"]  // Linux 系统中可以正确编译执行
class mc{}main(): Int64 {var a = mc()return 0
}@When[os == "Linux"]
func foo() {print("Linux, ")
}
@When[os == "Windows"]
func foo() {print("Windows, ")
}
@When[os != "Windows"]
func fee() {println("NOT Windows")
}
@When[os != "Linux"]
func fee() {println("NOT Linux")
}
main() {foo()fee()
}

FastNative

为了提升与 C 语言互操作的性能,仓颉提供 @FastNative 标记用于优化对 C 函数的调用。值得注意的是 @FastNative 只能用于 foreign 声明的函数。

@FastNative
foreign func strlen(str: CPointer<UInt8>): UIntNative

反射

仓颉的动态特性主要包含反射、动态加载。

反射需要TypeInfo这个核心类型:

public class TypeInfo {public static func of(a: Any): TypeInfopublic static func of(a: Object): ClassTypeInfopublic static func of<T>(): TypeInfo
}let t1: TypeInfo = TypeInfo.get("Int64")  // 必须已经被实例化了import std.reflect.*public class Foo {public static var param1 = 20public var param2 = 10
}main(): Unit{let obj = Foo()let info = TypeInfo.of(obj)let staticVarInfo = info.getStaticVariable("param1")let instanceVarInfo = info.getInstanceVariable("param2")println("成员变量初始值")print("Foo 的静态成员变量 ${staticVarInfo} = ")println((staticVarInfo.getValue() as Int64).getOrThrow())print("obj 的实例成员变量 ${instanceVarInfo} = ")println((instanceVarInfo.getValue(obj) as Int64).getOrThrow())println("更改成员变量")staticVarInfo.setValue(8)instanceVarInfo.setValue(obj, 25)print("Foo 的静态成员变量 ${staticVarInfo} = ")println((staticVarInfo.getValue() as Int64).getOrThrow())print("obj 的实例成员变量 ${instanceVarInfo} = ")println((instanceVarInfo.getValue(obj) as Int64).getOrThrow())return
}

动态加载

动态加载指的是仓颉程序可以在运行过程中通过特定函数来访问仓颉动态模块,以此读写全局变量、调用全局函数、获取类型信息的能力。主要通过 ModuleInfoPackageInfo 这两个类型来提供动态加载的能力。

例如我们存在一个 module0 模块下的 package0 包含一个公开的类型 Foo,其对应的仓颉动态模块路径为 “./module_package.so”

let m = ModuleInfo.load("./module_package")
let p = m.getPackageInfo("package0").getOrThrow()
let at = TypeInfo.get("module0/package0.Foo")

注释

一些属性宏用来支持一些特殊情况的处理。
仓颉中提供三种属性宏来控制整数溢出的处理策略,即 @OverflowThrowing@OverflowWrapping@OverflowSaturating

发者可以通过自定义类型标注 @Annotation 方式创建自己的自定义注解。@Annotation 只能修饰 class,并且不能是 abstractopensealed 修饰的 class。当一个 class 声明它标注了 @Annotation,那么它必须要提供至少一个 const init 函数,否则编译器会报错。

仓颉与C交互

仓颉调用 C 的函数

在仓颉中要调用 C 的函数,需要在仓颉语言中用 @Cforeign 关键字声明这个函数,但 @C 在修饰 foreign 声明的时候,可以省略。
需要注意的是:

  1. foreign 修饰函数声明,代表该函数为外部函数。被 foreign 修饰的函数只能有函数声明,不能有函数实现。
  2. foreign 声明的函数,参数和返回类型必须符合 C 和仓颉数据类型之间的映射关系。
  3. 由于 C 侧函数很可能产生不安全操作,所以调用 foreign 修饰的函数需要被 unsafe 块包裹,否则会发生编译错误。
  4. @C 修饰的 foreign 关键字只能用来修饰函数声明,不可用来修饰其他声明,否则会发生编译错误。
  5. @C 只支持修饰 foreign 函数、top-level 作用域中的非泛型函数和 struct 类型。
  6. foreign 函数不支持命名参数和参数默认值。foreign 函数允许变长参数,使用 ... 表达,只能用于参数列表的最后。变长参数均需要满足 CType 约束,但不必是同一类型。
  7. 仓颉(CJNative 后端)虽然提供了栈扩容能力,但是由于 C 侧函数实际使用栈大小仓颉无法感知,所以 ffi 调用进入 C 函数后,仍然存在栈溢出的风险,需要开发者根据实际情况,修改 cjStackSize 的配置。

假设有2个c的函数

// stdlib.h
int rand();// stdio.h
int printf (const char *fmt, ...);

需要在cangjie中声明,然后才能使用:

// declare the function by `foreign` keyword, and omit `@C`
foreign func rand(): Int32
foreign func printf(fmt: CString, ...): Int32main() {// call this function by `unsafe` blocklet r = unsafe { rand() }println("random number ${r}")unsafe {var fmt = LibC.mallocCString("Hello, No.%d\n")printf(fmt, 1)LibC.free(fmt)}
}

综合的demo

foreign func malloc(size: UIntNative): CPointer<Unit>
foreign func free(ptr: CPointer<Unit>): Unit@C
struct Point3D {var x: Int64var y: Int64var z: Int64init(x: Int64, y: Int64, z: Int64) {this.x = xthis.y = ythis.z = z}
}main() {let p1 = CPointer<Point3D>() // create a CPointer with null valueif (p1.isNull()) {  // check if the pointer is nullprint("p1 is a null pointer")}let sizeofPoint3D: UIntNative = 24var p2 = unsafe { malloc(sizeofPoint3D) }    // malloc a Point3D in heapvar p3 = unsafe { CPointer<Point3D>(p2) }    // pointer type castunsafe { p3.write(Point3D(1, 2, 3)) } // write data through pointerlet p4: Point3D = unsafe { p3.read() } // read data through pointerlet p5: CPointer<Point3D> = unsafe { p3 + 1 } // offset of pointerunsafe { free(p2) }
}

类型匹配

Cangjie TypeC TypeSize (byte)
Unitvoid0
Boolbool1
UInt8char1
Int8int8_t1
UInt8uint8_t1
Int16int16_t2
UInt16uint16_t2
Int32int32_t4
UInt32uint32_t4
Int64int64_t8
UInt64uint64_t8
IntNativessize_tplatform dependent
UIntNativesize_tplatform dependent
Float32float4
Float64double8

对于指针

// c
void* malloc(size_t size);// cangjie
foreign func malloc(size: UIntNative): CPointer<Unit>

结构体

// c
typedef struct {long long x;long long y;long long z;
} Point3D;// cangjie
@C
struct Point3D {var x: Int64 = 0var y: Int64 = 0var z: Int64 = 0
}

函数

// c
Point3D addPoint(Point3D p1, Point3D p2);// cangjie
foreign func addPoint(p1: Point3D, p2: Point3D): Point3D

如果是函数指针:

// Case 1
foreign func free(ptr: CPointer<Int8>): Unit// Case 2
@C
func callableInC(ptr: CPointer<Int8>) {print("This function is defined in Cangjie.")
}// Case 3
let f1: CFunc<(CPointer<Int8>) -> Unit> = { ptr =>print("This function is defined with CFunc lambda.")
}

数组

可以是 CPointer<T> 类型或 VArray<T, $N> 类型。

// c
void cfoo1(int *a) { ... }
void cfoo2(int a[3]) { ... }// cangjie
foreign func cfoo1(a: CPointer<Int32>): Unit
foreign func cfoo2(a: VArray<Int32, $3>): Unit

结构体数组

// c
struct S {int a[2];int b[0];
}// cangjie
@C
struct S {var a = VArray<Int32, $2>(repeat: 0)var b = VArray<Int32, $0>(repeat: 0)
}

字符串

仓颉中设计了一个 CString 类型来对应。为简化为 C 语言字符串的操作,CString 提供了以下成员函数:

  • init(p: CPointer<UInt8>) 通过 CPointer 构造一个 CString
  • func getChars() 获取字符串的地址,类型为 CPointer<UInt8>
  • func size(): Int64 计算该字符串的长度
  • func isEmpty(): Bool 判断该字符串的长度是否为 0,如果字符串的指针为空返回 true
  • func isNotEmpty(): Bool 判断该字符串的长度是否不为 0,如果字符串的指针为空返回 false
  • func isNull(): Bool 判断该字符串的指针是否为 null
  • func startsWith(str: CString): Bool 判断该字符串是否以 str 开头
  • func endsWith(str: CString): Bool 判断该字符串是否以 str 结尾
  • func equals(rhs: CString): Bool 判断该字符串是否与 rhs 相等
  • func equalsLower(rhs: CString): Bool 判断该字符串是否与 rhs 相等,忽略大小写
  • func subCString(start: UInt64): CString 从 start 开始截取子串,返回的子串存储在新分配的空间中
  • func subCString(start: UInt64, len: UInt64): CString 从 start 开始截取长度为 len 的子串,返回的子串存储在新分配的空间中
  • func compare(str: CString): Int32 该字符串与 str 比较,返回结果与 C 语言的 strcmp(this, str) 一样
  • func toString(): String 用该字符串构造一个新的 String 对象
  • func asResource(): CStringResource 获取 CString 的 Resource 类型

另外,将 String 类型转换为 CString 类型,可以通过调用 LibC 中的 mallocCString 接口,使用完成后需要对 CString 进行释放。

foreign func strlen(s: CString): UIntNativemain() {var s1 = unsafe { LibC.mallocCString("hello") }var s2 = unsafe { LibC.mallocCString("world") }let t1: Int64 = s1.size()let t2: Bool = s2.isEmpty()let t3: Bool = s1.equals(s2)let t4: Bool = s1.startsWith(s2)let t5: Int32 = s1.compare(s2)let length = unsafe { strlen(s1) }unsafe {LibC.free(s1)LibC.free(s2)}
}

C 调用仓颉的函数

在仓颉侧构造 CFunc 类型有两种办法,一个是用 @C 修饰的函数,另外一个是标记为 CFunc 类型的闭包。

其余的与仓颉调用C一样。

仓颉-Python 互操作

目前 Python 互操作仅支持在 Linux 平台使用,并且仅支持仓颉编译器的 cjnative 后端。通过 std 模块中的 ffi.python 库为用户提供能力。通过 std 模块中的 ffi.python 库为用户提供能力。

import std.ffi.python.*
import std.collection.*main(): Int64 {Python.load()// Create an unavailable value.var a = Python.Eval("a = 10")   // SyntaxError: invalid syntaxprint("${a.isAvailable()}\n")   // false// Uncallable value `b` be invokedvar b = Python.Eval("10")b()                           // TypeError: 'int' object is not callable// Import .py file.var test = Python.Import("test01")// `get []` get value of `a`.var p_a = test["a"]print("${p_a}\n")               // 10// `set []` set the value of a to 20.test["a"] = Python.Eval("20")test["function"]()            // a is 20// Call function02 with a named argument.test["function02"]([1], HashMap<String, PyObj>([("c", 2.toPyObj())]))// Set `a` in test01 to an unavailable value, and `a` will be deleted.test["a"] = atest["function"]()            // NameError: name 'a' is not definedPython.unload()0
}

cjpm.toml

[package]cjc-version = "0.49.1" # 所需 `cjc` 的最低版本要求,必需name = "demo" # 模块名及模块 root 包名,必需description = "nothing here" # 描述信息,非必需version = "1.0.0" # 模块版本信息,必需compile-option = "" # 额外编译命令选项,非必需link-option = "" # 链接器透传选项,可透传安全编译命令,非必需output-type = "executable" # 编译输出产物类型,必需src-dir = "" # 指定源码存放路径,非必需target-dir = "" # 指定产物存放路径,非必需package-configuration = {} # 单包配置选项,非必需[workspace] # 工作空间管理字段,与 package 字段不能同时存在members = []build-members = []test-members = []compile-option = ""link-option = ""target-dir = ""[dependencies] # 源码依赖配置项coo = { git = "xxx",branch = "dev" , version = "1.0.0"} # 导入 `git` 依赖,`version`字段可缺省doo = { path = "./pro1" ,version = "1.0.0"} # 导入源码依赖,`version`字段可缺省[test-dependencies] # 测试阶段的依赖配置项[ffi.c] # 导入 `c` 库依赖clib1.path = "xxx"[profile] # 命令剖面配置项build = {}test = {}customized-option = {}[target.x86_64-unknown-linux-gnu] # 后端和平台隔离配置项compile-option = "value1" # 额外编译命令选项,适用于特定 target 的编译流程和指定该 target 作为交叉编译目标平台的编译流程,非必需link-option = "value2" # 链接器透传选项,适用于特定 target 的编译流程和指定该 target 作为交叉编译目标平台的编译流程,非必需[target.x86_64-w64-mingw32.dependencies] # 适用于对应 target 的源码依赖配置项,非必需[target.x86_64-w64-mingw32.test-dependencies] # 适用于对应 target 的测试阶段依赖配置项,非必需[target.cjvm.bin-dependencies] # 仓颉二进制库依赖,适用于特定 target 的编译流程和指定该 target 作为交叉编译目标平台的编译流程,非必需path-option = ["./test/pro0", "./test/pro1"]
[target.cjvm.bin-dependencies.package-option]"pro0.xoo" = "./test/pro0/pro0.xoo.cjo""pro0.yoo" = "./test/pro0/pro0.yoo.cjo""pro1.zoo" = "./test/pro1/pro1.zoo.cjo"

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

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

相关文章

时间序列预测(十八)——实现配置管理和扩展命令行参数解析器

如图&#xff0c;这是一个main,py文件&#xff0c;在此代码中&#xff0c;最开始定义了许多模型参数&#xff0c;为了使项目更加灵活和可扩展&#xff0c;便于根据不同的需求调整参数和配置&#xff0c;可以根据实际需要扩展参数和配置项。 下面是如何实现配置管理和扩展命令行…

Ubuntu用docker安装AWVS和Nessus(含破解)

Ubuntu安装AWVS(更多搜索&#xff1a;超详细Ubuntu用docker安装AWVS和Nessus) 首先安装docker&#xff0c;通过dockers镜像安装很方便&#xff0c;且很快&#xff1b;Docker及Docker-Compose-安装教程。 1.通过docker search awvs命令查看镜像&#xff1b; docker search awvs…

QT for android 问题总结(QT 5.15.2)

1.配置好的sdk&#xff0c;显示设置失败 Android SDK Command-line Tools run. Android Platform-Tools installed. Command-line Tools (latest) 版本过高导致报错 &#xff0c;下载一个低版本的latest &#xff0c;替换掉之前latest中的文件。即可&#xff0c;latest 路径如…

Jmeter5.X性能测试

Jmeter5.X性能测试 文章目录 Jmeter5.X性能测试一、掌握Http基础协议1.1 浏览器的B/S架构和C/S架构1.2 HyperText Transfer Protocol 超文本传输协议1.3 超文本传输协议Http消息体拆分讲解1.4 HTTP的九种请求方法和响应码介绍1.5 Http请求头/响应头1.6 Http常见请求/响应头cont…

软件测试基础九 (python基础)

python基础 1. 注释 1.1. 注释的作⽤ 使⽤⾃⼰熟悉的语⾔&#xff0c;在程序中对某些代码进⾏标注说明&#xff0c;增强程序的可读性。 1.2. 单⾏注释 以# 开头&#xff0c;# 右边的所有东⻄都被当做说明⽂字&#xff0c;⽽不是真正要执⾏的程序&#xff0c;只起到辅助说明…

使用Kafka构建大规模消息传递系统

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Kafka构建大规模消息传递系统 引言 Kafka 简介 安装 Kafka 创建主题 生产者 消费者 高级特性 分区 持久化 消费者组 消息确认…

AI 大模型重塑软件开发流程的未来

目录 前言1. AI 大模型简介2. AI 大模型在软件开发中的应用场景2.1 代码自动生成2.2 智能调试与错误检测2.3 软件测试的自动化2.4 文档生成与代码注释 3. AI 大模型对软件开发的优势3.1 提高开发效率3.2 提升代码质量与一致性3.3 降低学习成本 4. AI 大模型在软件开发中面临的挑…

docker镜像获取不到的问题处理

总结 国内源的限制&#xff0c;很多镜像pull失败。 我目前使用的感觉最合理的方式就是去云平台厂商买一个香港的云主机。使用弹性模式&#xff0c;就是一小时几毛钱的那种。 然后pull镜像&#xff0c;pull成功后&#xff0c;save到本地&#xff0c;然后用xshell下载下来。因为…

C#-类:声明类、声明类对象

一&#xff1a;类的声明 class 类名 {//特征——成员变量//行为——成员方法//保护特征——成员属性//构造函数和析构函数//索引器//运算符重载//静态成员 }类名&#xff1a;帕斯卡 同一个语句块中的不同类 不能重名 二&#xff1a;声明类对象 2.1 类的声明 ≠ 类对象的声…

求平面连接线段组成的所有最小闭合区间

这个功能确实非常实用&#xff0c;我在过去开发地面分区编辑器时就曾应用过这一算法。最近&#xff0c;在新产品的开发中再次遇到了类似的需求。尽管之前已经实现过&#xff0c;但由于长时间未接触&#xff0c;对算法的具体细节有所遗忘&#xff0c;导致重新编写时耗费了不少时…

【P2-7】ESP8266 WIFI模块在AP模式下实现UDP与电脑/手机网络助手通信——UDP数据透传

前言:完成ESP8266 WIFI模块在AP模式下实现UDP与电脑/手机网络助手通信——实现UDP数据透传 AP模式,通俗来说模块可以发出一个WIFI热点提供给电脑/手机连接。 UDP协议,是传输层协议,UDP没有服务器和客户端的说法。 演示视频: ESP8266 WIFI模块在AP模式下实现UDP与电脑/手机…

金箍棒变化-第15届蓝桥杯国赛Scratch初/中级组真题第1题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第193讲。 如果想持续关注Scratch蓝桥真题解读&#xff0c;可以点击《Scratch蓝桥杯历年真题》并订阅合集&#xff0c;…

SpringBoot+Shirp的权限管理

目录 怎么实现动态菜单 1.html页面 2.获取动态菜单 Shiro权限刷新 1. 配置Shiro 2. 创建权限刷新服务 3. 调用权限刷新服务 注意事项 如何更新ShiroFilter初始权限 怎么实现动态菜单 1.html页面 <ul class"nav side-menu"><!--第一重循环&#xf…

hunyuan-DiT模型部署指南

一、介绍 Hunyuan-DiT是由腾讯混元推出的扩散模型&#xff0c;支持中文和英文双语输入&#xff0c;其他开源模型相比&#xff0c;Hunyuan-DiT 在中文到图像生成方面树立了新的水平。 二、部署流程 环境要求&#xff1a; 所需的最小 GPU 内存为 11GB&#xff0c; 建议使用具…

如何彻底删除gitbash中所有的命令记录、以及彻底删除Windows powerShell或者cmd中的所有命令记录

文章目录 1. 文章引言2. 彻底删除gitbash中所有的命令记录3. 彻底删除Windows powerShell或者cmd中的所有命令记录1. 文章引言 有时,我们使用外部电脑从gitbash中下载代码,假设使用history -c命令: 可以清除当前弹框的历史记录,但也无法彻底删除命令记录。打开gitbash后,通…

安全性测试

安全性测试评估系统在面对各种安全威胁时的防护能力和安全性的过程。以下是安全性测试的一些主要方面和方法&#xff1a; 1. 身份验证和授权测试 测试目标 确保系统能够正确验证用户的身份&#xff0c;并根据用户的权限授予相应的访问权限。测试方法 弱密码测试&#xff1a;尝…

利用 Feather 格式加速数据科学工作流:Pandas 中的最佳实践

利用 Feather 格式加速数据科学工作流&#xff1a;Pandas 中的最佳实践 在数据科学中&#xff0c;高效的数据存储和传输对于保持分析流程的流畅性至关重要。传统的 CSV 格式虽然通用&#xff0c;但在处理大规模数据集时速度较慢&#xff0c;特别是在反复读取和写入时。幸运的是…

Redis-“自动分片、一定程度的高可用性”(sharding水平拆分、failover故障转移)特性(Sentinel、Cluster)

文章目录 零、写在前面一、水平拆分(sharding/分片)、故障转移(failover)机制介绍水平拆分&#xff08;Sharding&#xff09;故障转移机制 二、Redis的水平拆分的机制有关的配置1. 环境准备2. 配置文件配置3. 启动所有Redis实例4. 创建集群5. 测试集群读/写6. 集群管理 三、Red…

Linux SSH免密登入以及配置脚本

一、ssh原理简单介绍 客户端生成一对公钥和私钥&#xff0c;并将自己的公钥发送到服务器上 其中公钥用来加密&#xff0c;私钥用来解密。 二、ssh免密登入实现步骤详解 我这就以服务器controller和客户端compute来做为例子 2.1、首先在controller上输入ssh-keygen -t rsa …

什么是信息安全管理体系?

信息安全管理体系&#xff08;ISMS&#xff0c;Information Security Management System&#xff09;是组织在整体或特定范围内建立的信息安全方针和目标&#xff0c;以及完成这些目标所用的方法和手段所构成的体系。以下是对ISMS的详细解释&#xff1a; 一、定义与背景ISMS是信…