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

【跟小嘉学 Rust 编程】十五、智能指针

系列文章目录

【跟小嘉学 Rust 编程】一、Rust 编程基础
【跟小嘉学 Rust 编程】二、Rust 包管理工具使用
【跟小嘉学 Rust 编程】三、Rust 的基本程序概念
【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念
【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据
【跟小嘉学 Rust 编程】六、枚举和模式匹配
【跟小嘉学 Rust 编程】七、使用包(Packages)、单元包(Crates)和模块(Module)来管理项目
【跟小嘉学 Rust 编程】八、常见的集合
【跟小嘉学 Rust 编程】九、错误处理(Error Handling)
【跟小嘉学 Rust 编程】十一、编写自动化测试
【跟小嘉学 Rust 编程】十二、构建一个命令行程序
【跟小嘉学 Rust 编程】十三、函数式语言特性:迭代器和闭包
【跟小嘉学 Rust 编程】十四、关于 Cargo 和 Crates.io
【跟小嘉学 Rust 编程】十五、智能指针

文章目录

  • 系列文章目录
    • @[TOC](文章目录)
  • 前言
  • 一、智能指针
    • 1.1、智能指针(smart point)
    • 1.2、Box 堆内存分配
      • 1.2.1、场景1:堆内存上分配数据
      • 1.2.2、场景2: cons list
    • 1.3、Deref 解引用
      • 1.3.1、Deref trait
      • 1.3.2、三种 Deref 转换
    • 1.4、Drop 释放资源
      • 1.4.1、Drop trait
      • 1.4.2、使用 std::mem::drop 来提前 drop
    • 1.5、引用计数智能指针(`RC<T>` 和 `Arc<T>`)
      • 1.5.1、`RC<T>`
      • 1.5.1、原子引用计数(Atomic reference counter)
    • 1.6、Cell 与 RefCell 内部可变性
      • 1.6.1、内部可变性(interior mutability)
      • 1.6.2、Cell<T>
      • 1.6.3、RefCell<T>
      • 1.6.4、Cell 和 RefCell
      • 1.6.5、解决借用冲突
    • 1.7、Weak 和引用循环
      • 1.7.1、引用循环和内存泄漏
      • 1.7.2、Weak
      • 1.7.3、unsafe
  • 总结

前言

指针是一个包含了内存地址的变量,该内存地址引用或执行了另外的数据。在Rust中最常见的指针类型就是引用。不同的是在Rust中引用被赋予更深的含义就是借用其他变量的值。

主要教材参考 《The Rust Programming Language》


一、智能指针

1.1、智能指针(smart point)

智能指针是一个复杂的数据结构,包含了比引用更多的信息,例如元数据,当前长度,最大可用长度等。

在之前章节实际上我们已经见识过多种智能指针了,例如动态字符串 String 和动态数据 Vec。

智能指针往往是基于结构体实现,它与我们自定义的结构体最大的区别在于它实现了 Deref 和 Drop 特征:

  • Deref:可以让智能指针像引用那样工作,这样你就可以写出同时支持智能指针和引用的代码,例如 *T
  • Drop:允许你就指定智能指针超出作用域后自动执行的代码,例如数据清理等收尾工作

1.2、Box 堆内存分配

在Rust 中,所有值默认都是在栈内存上分配,通过创建 Box<T> 可用把值装箱,使它在堆上分配。Box<T> 是一个智能指针,因为它实现了 Deref trait,它允许Box<T> 值被当作引用对待,当 Box<T> 值离开作用域时,由于它实现了 Drop trait ,首先删除其指向堆堆数据,然后删除自身。

使用场景

  • 在编译时,某类型的大小无法确定,但使用该类型时,上下文却需要知道它确切的大小;
  • 当你有大量数据,想移交所有权,但需要确保在操作时数据不会被复制;
  • 使用某个值,你只关心它是否实现了特定的 trait ,而不关心它的具体类型;

1.2.1、场景1:堆内存上分配数据


fn main() {let a = Box::new(1);  // Immutableprintln!("{}", a);    // Output: 1let mut b = Box::new(1);  // Mutable*b += 1;println!("{}", b);    // Output: 2
}

Box 的主要特性是单一所有权,即同时智能有一个人拥有对其指向数据的所有权,并且同时智能存在一个可变引用或多个不可变引用,这一点与Rust中其他属于堆上的数据行为一致。

1.2.2、场景2: cons list

cons list 是来自 Lisp 语言的一种数据结构。cons list 里面每个成员都包含两个元素:当前项都值和下一个元素。cons list 里的最后一个成员只包含一个 nil 值,没有下一个元素。

Box<T> 是一个指针,Rust知道它需要多少空间,因为指针的大小不会基于它指向的数据的大小变化而变化。

use crate::List::{Cons, Nil};fn main() {let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3,Box::new(Nil))))));
}enum List {Cons(i32, Box<List>),Nil,
}

1.3、Deref 解引用

1.3.1、Deref trait

Deref Trait 允许我们重载解引用运算符 *。实现 Deref 的智能指针可以被当作引用来对待,也就是说可以对智能指针使用 *运算符来解引用。

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Deref for Box<T> {type Target = T;fn deref(&self) -> &T {&**self}
}

1.3.2、三种 Deref 转换

在之前,我们讲的都是不可变的 Deref 转换,实际上 Rust 还支持将一个可变的引用转换成另一个可变的引用以及将一个可变引用转换成不可变的引用,规则如下:

当 T: Deref<Target=U>,可以将 &T 转换成 &U,也就是我们之前看到的例子
当 T: DerefMut<Target=U>,可以将 &mut T 转换成 &mut U
当 T: Deref<Target=U>,可以将 &mut T 转换成 &U

1.4、Drop 释放资源

1.4.1、Drop trait

Drop trait 主要作用是释放实现者实例拥有的资源,它只有一个方法 drop。当实例离开作用域时会自动调用该方法,从而调用实现者指定的代码。

#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized> Drop for Box<T> {fn drop(&mut self) {// FIXME: Do nothing, drop is currently performed by compiler.}
}

1.4.2、使用 std::mem::drop 来提前 drop

Rust 不允许手动调用 Drop trait 的 drop 方法,但是可以 使用标准库的 std::mem::drop 来提前 drop。

1.5、引用计数智能指针(RC<T>Arc<T>)

1.5.1、RC<T>

RC<T> 主要用于同一个堆上所有分配的数据区域需要多个只读访问的情况,比起使用比起使用 Box<T> 然后创建多个不可变引用的方法更优雅也更直观一些,以及比起单一所有权,Rc<T> 支持多所有权。

Rc 为 Reference Counter 的缩写,即为引用计数,Rust 的 Runtime 会实时记录一个 Rc<T> 当前被引用的次数,并在引用计数归零时对数据进行释放(类似 Python 的 GC 机制)。因为需要维护一个记录 Rc<T> 类型被引用的次数,所以这个实现需要 Runtime Cost。

use std::rc::Rc;fn main() {let a = Rc::new(1);println!("count after creating a = {}", Rc::strong_count(&a));let b = Rc::clone(&a);println!("count after creating b = {}", Rc::strong_count(&a));{let c = Rc::clone(&a);println!("count after creating c = {}", Rc::strong_count(&a));}println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}

需要注意

  • RC<T> 是完全不可变,可以理解为同一个内存上的数据同时存在多个只读指针
  • RC<T> 只适用单线程,尽管从概念上讲不同线程间只读指针是完全安全的,但是由于 RC<T> 没有实现多个线程间保证计数一致性,如果你尝试多线程内使用,会报错;

1.5.1、原子引用计数(Atomic reference counter)

此时引用计数就可以在不同线程中安全的被使用了。

use std::thread;
use std::sync::Arc;fn main() {let a = Arc::new(1);thread::spawn(move || {let b = Arc::clone(&a);println!("{}", b);  // Output: 1}).join();
}

1.6、Cell 与 RefCell 内部可变性

1.6.1、内部可变性(interior mutability)

内部可变性(interior mutability) 是 Rust 的设计模式之一,它允许你在只持有不可变引用的前提下对数据进行修改,数据结构中使用了 unsafe 代码来绕过 Rust 正常的可变性和借用规则。

1.6.2、Cell

Cell 和 Refcell 在功能上没有区别,区别在于 Cell 适用于 T 实现 Copy 的情况

1.6.3、RefCell

由于 Cell 类型针对的是实现了 Copy 特征的值类型,因此在实际开发中,Cell 使用的并不多,因为我们要解决的往往是可变、不可变引用共存导致的问题,此时就需要借助于 RefCell 来达成目的。

Rust 规则智能指针带来的额外规则
一个数据只有一个所有者Rc/Arc 让一个数据可以拥有多个所有者
要么多个不可变借用,要么一个可变借用RefCell 实现编译器可变、不可变引用共存
违背规则导致编译错误违背规则导致运行时 panic

可以看出,Rc/Arc 和 RefCell 合在一起,解决了 Rust 中严苛的所有权和借用规则带来的某些场景下难使用的问题。但是它们并不是银弹,例如 RefCell 实际上并没有解决可变引用和引用可以共存的问题,只是将报错从编译期推迟到运行时,从编译器错误变成了 panic 异常:

1.6.4、Cell 和 RefCell

  • Cell 只适用于 Copy 类型,用于提供值,而RefCell 用于提供引用
  • Cell 不会panic ,而 RefCell 会
  • Cell 没有额外的性能损耗

从 CPU 来看,损耗如下:

  • 对 Rc 解引用是免费的(编译期),但是 * 带来的间接取值并不免费
  • 克隆 Rc 需要将当前的引用计数跟 0 和 usize::Max 进行一次比较,然后将计数值加 1
  • 释放(drop) Rc 需要将计数值减 1, 然后跟 0 进行一次比较
  • 对 RefCell 进行不可变借用,需要将 isize 类型的借用计数加 1,然后跟 0 进行比较
  • 对 RefCell 的不可变借用进行释放,需要将 isize 减 1
  • 对 RefCell 的可变借用大致流程跟上面差不多,但是需要先跟 0 比较,然后再减 1
  • 对 RefCell 的可变借用进行释放,需要将 isize 加 1

1.6.5、解决借用冲突

在 Rust 1.37 版本中新增了两个非常实用的方法:

  • Cell::from_mut,该方法将 &mut T 转为 &Cell
  • Cell::as_slice_of_cells,该方法将 &Cell<[T]> 转为 &[Cell]

1.7、Weak 和引用循环

1.7.1、引用循环和内存泄漏

Rust 的内存安全机制可以保证很难发生内存泄漏。但是不代表不会内存泄漏。一个典型的例子就是同时使用 Rc 和 RefCell 创建循环引用,最终这些引用的计数都无法被归零,因此 Rc 拥有的值也不会被释放清理。

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;#[derive(Debug)]
enum List {Cons(i32, RefCell<Rc<List>>),Nil,
}impl List {fn tail(&self) -> Option<&RefCell<Rc<List>>> {match self {Cons(_, item) => Some(item),Nil => None,}}
}fn main() {let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));println!("a的初始化rc计数 = {}", Rc::strong_count(&a));println!("a指向的节点 = {:?}", a.tail());// 创建`b`到`a`的引用let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));println!("在b创建后,a的rc计数 = {}", Rc::strong_count(&a));println!("b的初始化rc计数 = {}", Rc::strong_count(&b));println!("b指向的节点 = {:?}", b.tail());// 利用RefCell的可变性,创建了`a`到`b`的引用if let Some(link) = a.tail() {*link.borrow_mut() = Rc::clone(&b);}println!("在更改a后,b的rc计数 = {}", Rc::strong_count(&b));println!("在更改a后,a的rc计数 = {}", Rc::strong_count(&a));// 下面一行println!将导致循环引用// 我们可怜的8MB大小的main线程栈空间将被它冲垮,最终造成栈溢出// println!("a next item = {:?}", a.tail());
}

如何防止循环引用

  • 开发者去注意细节
  • 使用 Weak

1.7.2、Weak

Weak 类似 RC 但是和 RC持有所有权不同,Weak 不必持有所有权,仅仅保存一份指向数据的弱引用,如果你要想访问数据,需要通过 Weak 指针的 upgrade 方法实现,该方法返回个类型为 Option<Rc<T>> 的值。

所谓弱引用就是不保证引用关系存在,如果不存在,就返回None。

因为 Weak 引用不计入所有权,因此它无法阻止所引用的内存值被释放掉,而且 Weak 本身不对值的存在性做任何担保,引用的值还存在就返回 Some,不存在就返回 None。

WeakRC
不计数计数
不拥有所有权拥有值的所有权
不阻止值被释放(drop)所有权计数归零,才能drop
引用存在返回some,不存在返回None引用值必定存在
通过 upgrade 取到Option<Rc<T>> 再取值通过 Deref 自动解引用,取值无需任何操作

弱引用非常适合如下场景

  • 持有一个 Rc 对象的临时引用,并且不在乎引用的值是否依然存在
  • 阻止 Rc 导致的循环引用,因为 Rc 的所有权机制,会导致多个 Rc 都无法计数归零

1.7.3、unsafe

除了使用 Rust 标准库提供的这些类型,你还可以使用 unsafe 里的裸指针来解决这些棘手的问题,但是由于我们还没有讲解 unsafe。

虽然 unsafe 不安全,但是在各种库的代码中依然很常见用它来实现自引用结构,主要优点如下:

  • 性能高,毕竟直接用裸指针操作
  • 代码更简单更符合直觉: 对比下 Option<Rc<RefCell<Node>>>

总结

以上就是今天要讲的内容

相关文章:

【跟小嘉学 Rust 编程】十五、智能指针

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…...

Python爬虫基础之正则表达式

目录 一、什么是正则表达式&#xff1f; 二、re.compile()编译函数 三、group()获取匹配结果函数 四、常用匹配规则 4.1匹配单个字符 4.2匹配前字符次数 4.3匹配原生字符串 4.4匹配字符串开头和结尾 4.5分组匹配 五、re.match()开头匹配函数 六、re.search()全文搜索…...

【LeetCode】双指针妙解有效三角形的个数

Problem: 611. 有效三角形的个数 文章目录 题目分析讲解算法原理复杂度Code 题目分析 首先我们来分析一下本题的思路 看到题目中给出的示例 题目的意思很简单&#xff0c;就是将给到的数字去做一个组合&#xff0c;然后看看这三条边是否可以构成三角形。那判断的方法不用我说&a…...

mysql 计算两点之间距离

先说一下我们可能会用到的一些场景&#xff0c;这样同学们可以先评估&#xff0c;该篇文章是否对你有帮助&#xff01; 场景&#xff1a; 假设 美团&#xff0c;我点外卖时&#xff0c;系统会让我先进行定位&#xff0c;比如我定位在了 A 点&#xff0c;系统就会给我推荐&…...

c语言自定义头文件是什么情况下使用?一般在什么情况下引用自定义的头文件?一般在自定义头文件中写什么代码?

c语言自定义头文件是什么情况下使用&#xff1f;一般在什么情况下引用自定义的头文件&#xff1f;一般在自定义头文件中写什么代码&#xff1f; C语言自定义头文件是一种用来封装函数和变量声明的文件&#xff0c;它通常用于将一组相关的函数和变量的声明集中在一个地方&#…...

electron应用打包成功纪念一下

electron应用打包成功纪念一下&#xff0c;以前曾经行过后来打包各种报错&#xff0c;现在有空就尝试解决一下 首先安装nvm能够方便切换node版本 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash 顺利安装后你用nvm list查看node列表时会…...

远程办公中安全远程访问解决方案

什么是安全远程访问 安全的远程访问是一个至关重要的过程&#xff0c;可让您使用互联网从远处完全控制某人的设备。为了确保安全&#xff0c;为受保护的远程访问采取了额外的身份验证和加密措施。 为什么安全远程访问解决方案很重要 当 IT 技术人员从远处帮助人们解决计算机…...

StartUp启动框架-Android启动性能

简述 当谈论Android应用程序的启动性能时&#xff0c;StartUp启动框架是一个不可忽视的关键工具。它旨在优化应用程序的启动过程&#xff0c;确保用户在打开应用时能够迅速获得流畅、高效的体验。让我们来深入了解StartUp框架的作用和重要性&#xff0c;以及它是如何改善Andro…...

Positive Technologies:五分之四的网络攻击具有针对性

Positive Technologies 对 2023 年第二季度的相关网络威胁进行了分析。报告显示&#xff0c;自今年年初以来&#xff0c;有针对性的攻击数量增加了 10%&#xff0c;目前占 78%。专家们注意到利用漏洞的大规模攻击和大量用户个人数据的泄露。此外&#xff0c;在此期间&#xff0…...

clickhouse的另类表引擎

clickhouse常用的MergeTree引擎外&#xff0c;还有特殊的引擎 1&#xff0c;memory引擎&#xff0c;顾名思义&#xff0c;数据是存储在内存中&#xff0c;数据不会被压缩也不会倍格式化转换数据在内存中保存的形态与查询时看到的如出一辙&#xff0c;重启ck数据丢失 2&#xff…...

Uniapp新版本打包后覆盖安装,新增的页面无法跳转,需退出重新启动才可以打开的解决方案

最近写uniapp项目&#xff0c;发现一个坑&#xff0c;在新版本覆盖安装后直接打开APP&#xff0c;新增的页面竟然无法跳转&#xff0c;需要重新启动才可以正常打开&#xff0c;在网上查了很多方法&#xff0c;最终总结下来有以下几点&#xff1a; 1.看打的是debug包还是releas…...

系统架构设计高级技能 · 面向服务架构设计理论与实践

点击进入系列文章目录 系统架构设计高级技能 面向服务架构设计理论与实践 一、SOA的相关概念1.1SOA的定义1.2 业务流程与业务流程执行语言 二、SOA的发展史三、SOA与微服务的区别三、SOA的参考架构四、SOA的主要协议规范五、SOA的设计标准要求六、SOA的作用与设计原则七、SOA的…...

QT注册界面练习(信号与槽实现页面跳转)

一、注册界面练习思路以及具体代码 在完成注册页面搭建的前提下&#xff0c;通过信号与槽机制实现多组件之间的相互通信&#xff0c;实现页面跳转。 基本步骤&#xff1a; 首先&#xff0c;将注册页面的登录按钮与成功登陆信号绑定&#xff0c;当用户名与密码均匹配时&#xf…...

MySQL从入门到精通【进阶篇】之 主从复制详解

文章目录 0.前言1. 主从复制简介2. 主从复制的工作流程主从复制过程中的日志文件作用&#xff08;Binary Log&#xff09;和中继日志&#xff08;Relay Log&#xff09; 3. MySQL主从复制的配置4. 参考资料 0.前言 MySQL的主从复制和读写分离是数据库领域的基本概念&#xff0…...

vue使用qrcodejs2生成二维码

目录 概要 构建展示的vue组件qrcode.vue 组件的使用 概要 项目中用到需要展示二维码的样式&#xff0c;想到了qrcode 例如&#xff1a; 前提&#xff1a;安装包 npm install qrcodejs2 --save 构建展示的vue组件qrcode.vue <template><div style"width: …...

python注释

任何编程语言都少不了注释&#xff0c;Python也不例外&#xff0c;以下是Python注释的具体用法&#xff1a; 单行注释 Python编程语言的单行注释常以#开头&#xff0c;单行注释可以作为单独的一行放在被注释代码行之上&#xff0c;也可以放在语句或者表达式之后。 实例&…...

update-alternatives详解

1.功能作用 update-alternatives是dpkg的实用工具&#xff0c;用来维护系统命令的符号链接&#xff0c;以决定系统默认使用什么命令。 在Debian系统中&#xff0c;我们可能会同时安装有很多功能类似的程序和可选配置&#xff0c;如Web浏览器程序(firefox&#xff0c;konquero…...

JavaScript 编写更好的条件语句

在任何编程语言中&#xff0c;代码需要根据不同的条件在给定的输入中做不同的决定和执行相应的动作。 例如&#xff0c;在一个游戏中&#xff0c;如果玩家生命点为0&#xff0c;游戏结束。在天气应用中&#xff0c;如果在早上被查看&#xff0c;显示一个日出图片&#xff0c;如…...

聊聊PBE算法

序 本文主要研究一下PBE算法 PBE PBE即Password Based Encryption&#xff0c;基于口令的加密&#xff0c;它是一种组合算法&#xff0c;即一般是哈希对称算法&#xff0c;比如PBEWithMD5AndDES&#xff0c;就是用MD5做哈希&#xff0c;用DES做加解密&#xff0c;而其密钥则…...

用MFC打开外部程序

在MFC&#xff08;Microsoft Foundation Classes&#xff09;中&#xff0c;你可以使用ShellExecute函数来打开Notepad并加载指定的文件。ShellExecute函数是Windows API的一部分&#xff0c;它可以执行与操作系统相关的操作&#xff0c;例如打开文件、运行程序等。 以下是在M…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...