【一起学Rust | Tauri2.0框架】基于 Rust 与 Tauri 2.0 框架实现全局状态管理

前言

在现代应用程序开发中,状态管理是构建复杂且可维护应用的关键。随着应用程序规模的增长,组件之间共享和同步状态变得越来越具有挑战性。如果处理不当,状态管理可能会导致代码混乱、难以调试,并最终影响应用程序的性能和可扩展性。

Tauri 2.0 作为一个基于 Rust 的跨平台应用程序开发框架,为我们提供了一个强大的工具集来构建高性能、安全且易于维护的桌面应用程序。结合 Rust 语言的优势,我们可以实现高效且可靠的全局状态管理。

本文将深入探讨如何在 Tauri 2.0 应用程序中实现全局状态管理。我们将从基本概念开始,逐步介绍不同的状态管理方法,并通过实际代码示例演示如何在 Tauri 2.0 项目中应用这些方法。无论你是 Rust 和 Tauri 的新手,还是有经验的开发者,相信都能从本文中获得有价值的知识和实践经验。

文章目录

  • 前言
  • 一、全局状态管理概述
    • 1.1 全局状态管理的挑战
    • 1.2 全局状态管理的重要性
  • 二、Rust 与 Tauri 2.0 中的状态管理
    • 2.1 Rust 的所有权和借用机制
        • 2.2 Tauri 2.0 的架构
  • 三、Tauri 2.0 内置状态管理
    • 3.1 使用 `tauri::State`
    • 3.2 状态更新与事件
    • 3.3 使用异步互斥锁 (async mutex)
    • 3.4 使用`Arc`
    • 3.5 使用 Manager Trait来访问状态
    • 3.6 修复`Mismatching Types`
  • 四、使用第三方状态管理库
    • 4.1 `Redux` 启发的状态管理:`Yewdux`
    • 4.2 其他状态管理库
  • 五、状态管理的最佳实践
  • 六、实战案例:构建一个简单的计数器应用
    • 6.1 项目设置
    • 6.2 后端代码
    • 6.3 前端代码
    • 6.4 运行应用
  • 总结

一、全局状态管理概述

全局状态管理是指在应用程序的多个组件之间共享和同步数据的一种机制。这些数据可以是用户界面状态、应用程序配置、用户数据等。全局状态管理的目标是确保应用程序中的所有组件都能访问和更新相同的状态,从而保持数据的一致性和应用程序的整体协调性。

1.1 全局状态管理的挑战

在大型应用程序中,全局状态管理面临着以下挑战:

  1. 数据一致性: 确保所有组件都能访问和更新相同的状态,避免数据不一致导致的问题。
  2. 组件通信: 在不同的组件之间传递状态更新,确保所有相关组件都能及时响应状态变化。
  3. 性能优化: 避免不必要的状态更新和渲染,提高应用程序的性能。
  4. 代码可维护性: 保持状态管理代码的清晰和简洁,便于理解和维护。
  5. 可扩展性: 随着应用程序的增长,状态管理方案能够适应新的需求和变化。

1.2 全局状态管理的重要性

良好的全局状态管理可以带来以下好处:

  1. 简化组件开发: 组件无需关心状态的来源和更新,只需专注于自身的渲染和逻辑。
  2. 提高代码可维护性: 将状态管理逻辑集中处理,减少代码重复和冗余。
  3. 增强应用程序可预测性: 状态变化可追踪、可预测,便于调试和问题排查。
  4. 提升用户体验: 确保应用程序在不同组件之间保持一致的状态,提供流畅的用户体验。

二、Rust 与 Tauri 2.0 中的状态管理

Rust 语言的特性和 Tauri 2.0 框架的架构为我们提供了多种实现全局状态管理的方式。

2.1 Rust 的所有权和借用机制

Rust 的所有权和借用机制是其内存安全和并发安全的基础。在状态管理中,我们可以利用这些机制来确保状态数据在不同组件之间的安全共享和访问。

  • 所有权(Ownership): Rust 中的每个值都有一个被称为其所有者的变量。在任何给定时间,一个值只能有一个所有者。当所有者超出作用域时,该值将被丢弃。
  • 借用(Borrowing): 我们可以通过引用(&)来借用一个值,而无需获取其所有权。引用可以是可变的(&mut)或不可变的(&)。
  • 生命周期(Lifetime): 生命周期是 Rust 编译器用来确保引用始终有效的机制。
2.2 Tauri 2.0 的架构

Tauri 2.0 采用了一种基于 Web 技术(HTML、CSS、JavaScript)构建前端界面,并使用 Rust 编写后端逻辑的架构。这种架构使得我们可以利用 Web 生态系统中丰富的状态管理库,同时也能利用 Rust 的性能和安全性优势。

Tauri 2.0 提供了以下机制来实现前端和后端之间的通信和状态共享:

  1. 命令(Commands): 前端可以通过调用 Tauri 提供的命令来与后端进行交互。命令可以接收参数并返回结果。
  2. 事件(Events): 后端可以向前端发送事件,前端可以监听这些事件并做出响应。
  3. 状态(State): Tauri 2.0 提供了一个内置的状态管理机制,允许我们在后端管理全局状态,并在前端访问和更新这些状态。

三、Tauri 2.0 内置状态管理

Tauri 2.0 提供了一个简单而强大的内置状态管理机制,可以满足大多数应用程序的需求。

3.1 使用 tauri::State

tauri::State 是 Tauri 2.0 中用于管理全局状态的核心类型。它是一个泛型类型,可以存储任何实现了 SendSync trait 的类型。

  1. 定义状态类型:

    #[derive(Default)]
    struct AppState {counter: std::sync::Mutex<i32>,
    }
    

    这里我们定义了一个名为 AppState 的结构体,其中包含一个名为 counter 的字段。counter 的类型是 std::sync::Mutex<i32>,表示一个受互斥锁保护的 32 位整数。使用互斥锁可以确保多个线程安全地访问和修改 counter 的值。

  2. 初始化状态:

    fn main() {tauri::Builder::default().manage(AppState::default()).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    

    main 函数中,我们使用 tauri::Builder::manage 方法将 AppState 的一个实例注册为全局状态。

  3. 在命令中访问状态:

    #[tauri::command]fn increment_counter(state: tauri::State<AppState>) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;Ok(())}#[tauri::command]fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.lock().map_err(|e| e.to_string())?;Ok(*counter)}
  1. 在异步命令中访问状态:
    #[tauri::command]async fn increment_counter(state: tauri::State<AppState>) -> Result<(), String> {let mut counter = state.counter.await;*counter += 1;Ok(())}#[tauri::command]async fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.await;Ok(*counter)}

我们定义了两个命令:increment_counterget_counter。这两个命令都接收一个 tauri::State<AppState> 类型的参数,表示对全局状态的引用。

  • increment_counter 命令获取 counter 的互斥锁,将其值加 1,然后释放锁。
  • get_counter 命令获取 counter 的互斥锁,读取其值,然后释放锁并返回该值。
  1. 在前端访问状态:
   import { invoke } from '@tauri-apps/api/tauri';async function incrementCounter() {await invoke('increment_counter');updateCounter();}async function updateCounter() {const counter = await invoke('get_counter');document.getElementById('counter').textContent = counter;}// 在页面加载时更新计数器updateCounter();

在前端,我们使用 @tauri-apps/api/tauri 提供的 invoke 函数来调用后端命令。

  • incrementCounter 函数调用 increment_counter 命令,然后在状态更新后调用 updateCounter 函数。
  • updateCounter 函数调用 get_counter 命令获取计数器的当前值,并将其显示在页面上。

3.2 状态更新与事件

在上面的示例中,我们通过调用 get_counter 命令来获取状态的更新。这种方式在状态更新不频繁的情况下是可行的。但是,如果状态更新非常频繁,或者我们需要在状态更新时立即通知前端,那么使用事件机制会更有效。

  1. 在后端发送事件:

    #[tauri::command]
    fn increment_counter(state: tauri::State<AppState>, window: tauri::Window) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;window.emit("counter-updated", *counter).map_err(|e| e.to_string())?;Ok(())
    }
    

    increment_counter 命令中,我们在更新 counter 的值后,使用 window.emit 方法向前端发送一个名为 "counter-updated" 的事件,并将 counter 的当前值作为事件的负载。

  2. 在前端监听事件:

    import { invoke } from '@tauri-apps/api/tauri';
    import { listen } from '@tauri-apps/api/event';async function incrementCounter() {await invoke('increment_counter');
    }// 监听 counter-updated 事件
    listen('counter-updated', (event) => {document.getElementById('counter').textContent = event.payload;
    });// 在页面加载时更新计数器
    updateCounter();
    

    在前端,我们使用 @tauri-apps/api/event 提供的 listen 函数来监听 "counter-updated" 事件。当事件发生时,事件处理函数会被调用,并将事件的负载(即 counter 的当前值)显示在页面上。

3.3 使用异步互斥锁 (async mutex)

该功能必须tokio启用sync特征。

引用Tokio的文档,使用标准库的互斥体而不是Tokio提供的异步互斥体通常是可以的:

与普遍看法相反,在异步代码中使用标准库中的普通互斥体是可以的,而且通常是首选的……异步互斥体的主要用例是提供对IO资源(如数据库连接)的共享可变访问。

你需要充分阅读Tokio的文档以了解两者之间的权衡。需要使用异步互斥的一个原因是,如果您需要在await之间保持MutexGuard。

这种类型的作用类似于 std::sync::Mutex,有两个主要区别:lock 是一个异步方法,因此不会阻塞,并且锁保护被设计为跨 .await 点持有。

Tokio的Mutex在保证FIFO的基础上运行。 这意味着任务调用锁方法的顺序就是它们获取锁的顺序。

也就是说,你通常使用标准库的mutex基本上就能实现你的需求,因为async mutex实现起来成本高,因此Tokio官方直接就推荐使用标准库的mutex了。tokio文档中推荐使用:

  1. Arc<Mutex<...>>定义状态
  2. 生成一个task线程来与主线程通信

并且给出了样例代码

use tokio::sync::Mutex;
use std::sync::Arc;#[tokio::main]
async fn main() {let count = Arc::new(Mutex::new(0));for i in 0..5 {let my_count = Arc::clone(&count);tokio::spawn(async move {for j in 0..10 {let mut lock = my_count.lock().await;*lock += 1;println!("{} {} {}", i, j, lock);}});}loop {if *count.lock().await >= 50 {break;}}println!("Count hit 50.");
}

在这个例子中,有几件事需要注意。

  1. 互斥体被包裹在Arc中,以允许在线程之间共享。
  2. 每个生成的任务都会获得一个锁,并在每次迭代时释放它
  3. Mutex保护的数据的突变是通过取消引用所获得的锁来完成的。

Tokio的Mutex采用简单的FIFO(先进先出)风格,所有锁定调用都按照执行顺序完成。这样,互斥体在如何将锁分配给内部数据方面是“公平的”和可预测的。每次迭代后都会释放并重新获取锁,因此基本上,每个线程在递增一次值后都会转到行的后面。请注意,线程启动之间的时间存在一些不可预测性,但一旦它们启动,它们就会可预测地交替。最后,由于在任何给定时间只有一个有效的锁,因此在改变内部值时不可能出现竞争条件。

请注意,与std::sync::Mutex相反,当持有MutexGuard的线程崩溃时,此实现不会破坏互斥量。 在这种情况下,互斥锁将被解锁。 如果panic被捕获,这可能会使受互斥锁保护的数据处于不一致的状态。

3.4 使用Arc

在 Rust 中,常见用法是使用 Arc 在多个线程之间共享一个值的所有权(通常与 Mutex 配对使用,形式为 Arc<Mutex<T>>)。但是,你不需要对存储在 State 中的内容使用 Arc,因为 Tauri 会为你完成这项工作。

如果 State 的生命周期要求阻止你将状态移动到新线程中,你可以改为将 AppHandle 移动到线程中,然后检索你的状态,如下面“使用 Manager trait 访问状态”部分所示。AppHandle 特意设计成易于克隆,以用于此类用例。

3.5 使用 Manager Trait来访问状态

有时你可能需要在命令之外访问状态,例如在不同的线程中或在像 on_window_event 这样的事件处理程序中。在这种情况下,你可以使用实现了 Manager 特征的类型(例如 AppHandle)的 state() 方法来获取状态:

use tauri::{Builder, GlobalWindowEvent, Manager};#[derive(Default)]
struct AppState {counter: u32,
}// In an event handler:
fn on_window_event(event: GlobalWindowEvent) {// Get a handle to the app so we can get the global state.let app_handle = event.window().app_handle();let state = app_handle.state::<Mutex<AppState>>();// Lock the mutex to mutably access the state.let mut state = state.lock().unwrap();state.counter += 1;
}fn main() {Builder::default().setup(|app| {app.manage(Mutex::new(AppState::default()));Ok(())}).on_window_event(on_window_event).run(tauri::generate_context!()).unwrap();
}

当你不能依赖命令注入时,此方法非常有用。例如,如果你需要将状态移动到使用 AppHandle 更容易的线程中,或者你不在命令上下文中。

3.6 修复Mismatching Types

如果你为 State 参数使用了错误的类型,你将得到一个运行时 panic,而不是编译时错误。

例如,如果你使用 State<'_, AppState> 而不是 State<'_, Mutex<AppState>>,则不会有任何状态使用该类型进行管理。

如果你愿意,你可以用类型别名包装你的状态以防止这个错误:

use std::sync::Mutex;#[derive(Default)]
struct AppStateInner {counter: u32,
}type AppState = Mutex<AppStateInner>;

但是,请确保按原样使用类型别名,而不是再次将其包装在 Mutex 中,否则你将遇到同样的问题。

四、使用第三方状态管理库

除了 Tauri 2.0 的内置状态管理机制,我们还可以使用 Rust 生态系统中的第三方状态管理库来实现更复杂的状态管理需求。

4.1 Redux 启发的状态管理:Yewdux

Yewdux 是一个受 Redux 启发的 Rust 状态管理库,它提供了一种基于单向数据流的状态管理模式。

  1. 安装 Yewdux

    cargo add yewdux
    
  2. 定义状态和 Reducer

    use yewdux::prelude::*;#[derive(Default, Clone, PartialEq, Eq, Store)]
    struct AppState {counter: i32,
    }#[derive(Clone, PartialEq, Eq)]
    enum Action {Increment,
    }impl Reducer<AppState> for Action {fn apply(&self, mut state: Rc<AppState>) -> Rc<AppState> {let state = Rc::make_mut(&mut state);match self {Action::Increment => state.counter += 1,}state.clone().into()}
    }
    
    • 我们定义了一个名为 AppState 的结构体,其中包含一个 counter 字段。
    • 我们定义了一个名为 Action 的枚举,表示可以对状态执行的操作。
    • 我们为 Action 实现了 Reducer<AppState> trait,定义了如何根据不同的 Action 来更新状态。
  3. 在 Tauri 中使用 Yewdux

    use yewdux::prelude::*;#[tauri::command]
    fn increment_counter(dispatch: Dispatch<AppState>) -> Result<(), String> {dispatch.apply(Action::Increment);Ok(())
    }#[tauri::command]
    fn get_counter(dispatch: Dispatch<AppState>) -> Result<i32, String> {Ok(dispatch.get().counter)
    }fn main() {tauri::Builder::default().setup(|app| {let dispatch = Dispatch::<AppState>::new();app.manage(dispatch);Ok(())}).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    
    • main 函数中,我们使用 Dispatch::<AppState>::new() 创建一个 Dispatch 实例,并将其注册为 Tauri 的全局状态。
    • 在命令中,我们通过 Dispatch<AppState> 类型的参数来访问和修改状态。
    • increment_counter 命令使用 dispatch.apply(Action::Increment) 来触发状态更新。
    • get_counter 命令使用 dispatch.get().counter 来获取状态的当前值。
  4. 在前端使用 Yewdux

    与 Tauri 内置状态管理类似,我们可以使用 invoke 函数来调用后端命令,并通过事件或轮询来获取状态更新。

4.2 其他状态管理库

除了 Yewdux,Rust 生态系统中还有其他一些状态管理库可供选择,例如:

  • Relm4 一个基于 Elm 架构的 GUI 库,它内置了状态管理机制。
  • Iced 一个跨平台的 GUI 库,它也提供了自己的状态管理方案。

五、状态管理的最佳实践

在 Tauri 2.0 应用程序中实现全局状态管理时,可以遵循以下最佳实践:

  1. 选择合适的状态管理方案: 根据应用程序的复杂度和需求选择合适的状态管理方案。对于简单的应用程序,Tauri 2.0 的内置状态管理机制可能就足够了。对于更复杂的应用程序,可以考虑使用第三方状态管理库。
  2. 保持状态的单一数据源: 避免在多个地方维护相同的状态,确保状态的唯一性和一致性。
  3. 使用不可变数据: 尽可能使用不可变数据来表示状态,避免意外的状态修改。
  4. 最小化状态更新: 仅在必要时更新状态,避免不必要的状态更新和渲染。
  5. 使用选择器(Selectors): 如果状态数据比较复杂,可以使用选择器来从状态中提取所需的数据,避免在组件中直接访问原始状态。
  6. 使用调试工具: 利用 Tauri 2.0 和状态管理库提供的调试工具来跟踪状态变化和调试问题。
  7. 编写测试: 为状态管理逻辑编写单元测试和集成测试,确保状态管理的正确性和稳定性。

六、实战案例:构建一个简单的计数器应用

为了更好地理解如何在 Tauri 2.0 应用程序中实现全局状态管理,我们将构建一个简单的计数器应用。

6.1 项目设置

  1. 创建新的 Tauri 项目:

    cargo tauri init
    

    按照提示输入项目名称、窗口标题等信息。

  2. 安装 Tauri API:

    npm install @tauri-apps/api
    

6.2 后端代码

  1. 定义状态类型:

    // src-tauri/src/main.rs#[derive(Default)]
    struct AppState {counter: std::sync::Mutex<i32>,
    }
    
  2. 实现命令:

    // src-tauri/src/main.rs#[tauri::command]
    fn increment_counter(state: tauri::State<AppState>, window: tauri::Window) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;window.emit("counter-updated", *counter).map_err(|e| e.to_string())?;Ok(())
    }#[tauri::command]
    fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.lock().map_err(|e| e.to_string())?;Ok(*counter)
    }
    
  3. 注册状态和命令:

    // src-tauri/src/main.rsfn main() {tauri::Builder::default().manage(AppState::default()).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    

6.3 前端代码

  1. 创建 HTML 结构:

    <!-- src/index.html --><!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Tauri Counter App</title></head><body><h1>Counter: <span id="counter">0</span></h1><button id="increment-button">Increment</button><script src="main.js"></script></body>
    </html>
    
  2. 编写 JavaScript 代码:

    // src/main.jsimport { invoke } from '@tauri-apps/api/tauri';
    import { listen } from '@tauri-apps/api/event';async function incrementCounter() {await invoke('increment_counter');
    }// 监听 counter-updated 事件
    listen('counter-updated', (event) => {document.getElementById('counter').textContent = event.payload;
    });// 在页面加载时更新计数器
    async function updateCounter() {const counter = await invoke('get_counter');document.getElementById('counter').textContent = counter;
    }
    updateCounter()// 绑定按钮点击事件
    document.getElementById('increment-button').addEventListener('click', incrementCounter);
    

6.4 运行应用

cargo tauri dev

现在,你应该可以看到一个简单的计数器应用。点击 “Increment” 按钮,计数器的值会增加,并且界面会实时更新。

总结

全局状态管理是构建复杂 Tauri 2.0 应用程序的关键。本文深入探讨了 Tauri 2.0 中的全局状态管理,介绍了 Tauri 2.0 的内置状态管理机制以及如何使用第三方状态管理库。通过结合 Rust 语言的优势和 Tauri 2.0 框架的功能,我们可以构建高性能、安全且易于维护的桌面应用程序。

希望本文能够帮助你更好地理解 Tauri 2.0 中的全局状态管理,并在你的项目中应用这些知识。如果你有任何问题或建议,欢迎留言讨论。

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

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

相关文章

百度SEO和必应SEO优化方法

如需SEO服务&#xff0c;可以搜索&#xff1a;深圳市信科网络科技有限公司。 一、搜索引擎生态格局&#xff1a;流量入口的重新洗牌 2025 年&#xff0c;中国 PC 端搜索引擎市场正经历戏剧性变革。StatCounter 数据显示&#xff0c;必应凭借 Edge 浏览器的预装优势与 ChatGPT …

Redis 事件机制详解

Redis 事件机制详解 Redis 的事件机制是其高性能和高并发能力的关键之一&#xff0c;它采用Reactor 模型&#xff0c;基于文件事件驱动机制实现高效的 I/O 处理。Redis 的事件机制主要分为以下几类&#xff1a; 文件事件&#xff08;File Event&#xff09; —— 处理网络 I/…

【LangChain入门 3 Prompts组件】聊天提示词模板 ChatPromptTemplate

文章目录 一、 聊天信息提示词模板1.1 使用关键字1.2 使用SystemMessage, HumanMessage, AIMessage来定义消息1.3 使用MessagesPlaceholder 在特定未知添加消息列表 二、关键类介绍2.1 ChatPromptTemplate 类2.1.1 from_messages()2.1.2 format_messages()2.1.3 format_prompt(…

Flutter TextFormField 完全手册与设计最佳实践

目录 1. 引言 2. TextFormField 的基本用法 3. 主要属性 4. 自定义 TextFormField 样式 4.1 设置边框样式 4.2 设置输入格式限制 4.3 多行输入 5. 结论 相关推荐 1. 引言 在 Flutter 中&#xff0c;TextFormField 是 TextField 的扩展版本&#xff0c;专为表单输入设计…

HC-05与HC-06蓝牙配对零基础教程 以及openmv识别及远程传输项目的概述

这个是上一年的项目&#xff0c;之前弄得不怎么完整&#xff0c;只有一个openmv的&#xff0c;所以openmv自己去我主页找&#xff0c;这篇主要讲蓝牙 这个是我在使用openmv连接单片机1然后单片机1与单片机2通过蓝牙进行通信 最终实现的效果是&#xff1a;openmv识别到图形和数…

【Docker系列一】Docker 简介

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Vue 入门到实战 五

第5章 过渡与动画 目录 5.1 单元素/组件过渡 5.1.1 过渡class 5.1.2 CSS 过渡 5.1.3 CSS 动画 5.1.4 同时使用过渡和动画 5.1.5 JavaScript 钩子方法 5.2 多元素/组件过渡 5.2.1 多元素过渡 5.2.2 多组件过渡 5.3 列表过渡 5.3.1 列表的普通过渡 5.3.2 列表的平滑…

Apache SeaTunnel脚本升级及参数调优实战

最近作者针对实时数仓的Apache SeaTunnel同步链路&#xff0c;完成了双引擎架构升级与全链路参数深度调优&#xff0c;希望本文能够给大家有所启发&#xff0c;欢迎批评指正&#xff01; Apache SeaTunnel 版本 &#xff1a;2.3.9 Doris版本&#xff1a;2.0.6 MySQL JDBC Conne…

C++ 时间操作:获取有史以来的天数与文件计数器

C 时间操作&#xff1a;获取有史以来的天数与文件计数器 在C中&#xff0c;时间操作是一个非常重要的功能&#xff0c;尤其是在需要处理日期、时间戳或定时任务时。本文将介绍如何利用C的时间操作功能&#xff0c;实现以下两个目标&#xff1a; 获取从Unix纪元时间&#xff0…

Python Bug修复案例分析:Python 中常见的 IndentationError 错误 bug 的修复

在 Python 编程的世界里&#xff0c;代码的可读性和规范性至关重要。Python 通过强制使用缩进来表示代码块的层次结构&#xff0c;这一独特的设计理念使得代码更加清晰易读。然而&#xff0c;正是这种对缩进的严格要求&#xff0c;导致开发者在编写代码时&#xff0c;稍有不慎就…

【论文笔记】Transformer

Transformer 2017 年&#xff0c;谷歌团队提出 Transformer 结构&#xff0c;Transformer 首先应用在自然语言处理领域中的机器翻译任务上&#xff0c;Transformer 结构完全构建于注意力机制&#xff0c;完全丢弃递归和卷积的结构&#xff0c;这使得 Transformer 结构效率更高…

CI/CD(三) 安装nfs并指定k8s默认storageClass

一、NFS 服务端安装&#xff08;主节点 10.60.0.20&#xff09; 1. 安装 NFS 服务端 sudo apt update sudo apt install -y nfs-kernel-server 2. 创建共享目录并配置权限 sudo mkdir -p /data/k8s sudo chown nobody:nogroup /data/k8s # 允许匿名访问 sudo chmod 777 /dat…

【QA】单件模式在Qt中有哪些应用?

单例设计模式确保一个类仅有一个实例&#xff0c;并提供一个全局访问点来获取该实例。在 Qt 框架中&#xff0c;有不少类的设计采用了单例模式&#xff0c;以下为你详细介绍并给出相应代码示例。 1. QApplication QApplication 是 Qt GUI 应用程序的核心类&#xff0c;每个 Q…

存储过程触发器习题整理1

46、{blank}设有商品表(商品号&#xff0c;商品名&#xff0c;单价)和销售表(销售单据号&#xff0c;商品号&#xff0c;销售时间&#xff0c;销售数量&#xff0c;销售单价)。其中&#xff0c;商品号代表一类商品&#xff0c;商品号、单价、销售数量和销售单价均为整型。请编写…

基于ChatGPT、GIS与Python机器学习的地质灾害风险评估、易发性分析、信息化建库及灾后重建高级实践

第一章、ChatGPT、DeepSeek大语言模型提示词与地质灾害基础及平台介绍【基础实践篇】 1、什么是大模型&#xff1f; 大模型&#xff08;Large Language Model, LLM&#xff09;是一种基于深度学习技术的大规模自然语言处理模型。 代表性大模型&#xff1a;GPT-4、BERT、T5、Ch…

单表达式倒计时工具:datetime的极度优雅(智普清言)

一个简单表达式&#xff0c;也可以优雅自成工具。 笔记模板由python脚本于2025-03-22 20:25:49创建&#xff0c;本篇笔记适合任意喜欢学习的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 Pyth…

最优编码树的双子性

现在看一些书&#xff0c;不太愿意在书上面做一些标记&#xff0c;也没啥特殊的原因。。哈哈。 树的定义 无环连通图&#xff0c;极小连通图&#xff0c;极大无环图。 度 某个节点&#xff0c;描述它的度&#xff0c;一般默认是出度&#xff0c;分叉的边的条数。或者说孩子…

MiB和MB

本文来自腾讯元宝 MiB 和 ​MB 有区别&#xff0c;尽管它们都用于表示数据存储的单位&#xff0c;但它们的计算方式不同&#xff0c;分别基于不同的进制系统。 1. ​MiB&#xff08;Mebibyte&#xff09;​ ​MiB 是基于二进制的单位&#xff0c;使用1024作为基数。1 MiB 102…

Labview和C#调用KNX API 相关东西

叙述:完全没有听说过KNX这个协议...................我这次项目中也是简单的用了一下没有过多的去研究 C#调用示例工程链接(labview调用示例在 DEBUG文件夹里面) 通过网盘分享的文件&#xff1a;KNX调用示例.zip 链接: https://pan.baidu.com/s/1NQUEYM11HID0M4ksetrTyg?pwd…

损失函数理解(二)——交叉熵损失

损失函数的目的是为了定量描述不同模型&#xff08;例如神经网络模型和人脑模型&#xff09;的差异。 交叉熵&#xff0c;顾名思义&#xff0c;与熵有关&#xff0c;先把模型换成熵这么一个数值&#xff0c;然后用这个数值比较不同模型之间的差异。 为什么要做这一步转换&…