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

C# | 委托 | 事件 | 异步

委托(Delegate)和事件(Event)

在C#和C++中,委托(Delegate)与事件(Event)以及函数对象(Function Object)是实现回调机制或传递行为的重要工具。虽然它们在语法和具体实现上有所不同,但它们的核心思想是相似的:封装可调用的行为,以便可以在不同的上下文中执行。


1. C#中的委托(Delegate)

定义与作用
  • 委托是一种类型安全的函数指针,用于封装方法的引用。
  • 它允许将方法作为参数传递,或者存储方法以供后续调用。
  • 委托可以指向静态方法或实例方法,并且支持多播(Multicast),即一个委托可以同时调用多个方法。
基本语法
// 定义一个委托类型
public delegate void MyDelegate(string message);// 使用委托
public class Program
{public static void Main(){// 创建委托实例并绑定方法MyDelegate del = new MyDelegate(ShowMessage);del("Hello, World!");  // 调用委托}public static void ShowMessage(string message){Console.WriteLine(message);}
}
特点
  1. 类型安全:委托在编译时检查方法签名是否匹配。
  2. 多播支持:通过+=操作符,可以将多个方法绑定到同一个委托。
    MyDelegate del = ShowMessage;
    del += AnotherMethod;
    del("Hello");  // 会依次调用ShowMessage和AnotherMethod
    

2. C#中的事件(Event)

定义与作用
  • 事件是基于委托的一种特殊机制,通常用于实现发布-订阅模式。
  • 它限制了外部对委托的直接调用,只能通过+=-=来添加或移除事件处理程序。
基本语法
// 定义事件
public class Publisher
{// 声明事件public event EventHandler<MyEventArgs> Notify;public void DoSomething(){// 触发事件Notify?.Invoke(this, new MyEventArgs("Event Triggered"));}
}// 自定义事件参数类
public class MyEventArgs : EventArgs
{public string Message { get; }public MyEventArgs(string message){Message = message;}
}// 订阅事件
public class Subscriber
{public void OnNotify(object sender, MyEventArgs e){Console.WriteLine($"Received: {e.Message}");}
}// 使用示例
public class Program
{public static void Main(){Publisher publisher = new Publisher();Subscriber subscriber = new Subscriber();// 订阅事件publisher.Notify += subscriber.OnNotify;// 触发事件publisher.DoSomething();}
}
特点
  1. 封装性:事件对外部隐藏了委托的具体实现,只能通过+=-=操作。
  2. 松耦合:发布者和订阅者之间没有直接依赖,适合构建模块化的系统。

3. C++中的函数对象(Function Object)

定义与作用
  • 函数对象(也称为仿函数,Functor)是一个重载了operator()的类或结构体实例。
  • 它可以像函数一样被调用,同时具有普通对象的特性(如保存状态)。
基本语法
#include <iostream>
using namespace std;// 定义一个函数对象
class MyFunctor
{
public:void operator()(string message) const{cout << "Message: " << message << endl;}
};int main()
{// 创建函数对象实例MyFunctor functor;// 调用函数对象functor("Hello, World!");return 0;
}
特点
  1. 灵活性:函数对象可以保存状态,而普通函数不能。
    class Counter
    {
    private:int count;
    public:Counter() : count(0) {}void operator()(){cout << "Count: " << ++count << endl;}
    };int main()
    {Counter counter;counter();  // 输出 Count: 1counter();  // 输出 Count: 2return 0;
    }
    
  2. 性能优化:在某些情况下,函数对象比函数指针更高效,因为编译器可以对其进行内联优化。

4. 对比总结

特性C# 委托C# 事件C++ 函数对象
本质类型安全的函数指针基于委托的发布-订阅机制重载了operator()的对象
用途封装方法引用,支持多播实现事件驱动的通信机制封装行为,支持状态保存
类型安全
多播支持支持不直接支持不支持
状态保存不支持不支持支持
使用场景回调、异步编程GUI事件、观察者模式STL算法、自定义行为封装

5. 示例对比:实现简单的回调机制

C# 委托实现
public delegate void Callback(string message);public class Processor
{public Callback OnComplete;public void Process(){// 模拟处理逻辑OnComplete?.Invoke("Processing Complete");}
}public class Program
{public static void Main(){Processor processor = new Processor();processor.OnComplete = message => Console.WriteLine(message);processor.Process();  // 输出 Processing Complete}
}
C++ 函数对象实现
#include <iostream>
#include <functional>
using namespace std;class Processor
{
private:function<void(string)> callback;public:void SetCallback(function<void(string)> cb){callback = cb;}void Process(){if (callback)callback("Processing Complete");}
};int main()
{Processor processor;// 使用Lambda表达式作为函数对象processor.SetCallback([](string message) {cout << message << endl;});processor.Process();  // 输出 Processing Completereturn 0;
}

6. 总结

  • C#的委托和事件更适合面向对象的开发,尤其是在事件驱动的场景中(如GUI编程、异步任务)。
  • C++的函数对象则更加灵活,尤其在需要保存状态或与STL算法结合使用时表现出色。
  • 两者的核心思想都是封装行为,但在具体实现和应用场景上各有侧重。选择哪种方式取决于语言特性和实际需求。

一些简单的示例

委托(Delegate)和事件(Event)是C#中非常强大的机制,尤其是在实现回调、观察者模式、异步编程等场景时。以下是一些实用的例子,展示如何在实际开发中使用委托和事件。


1. 委托的实用例子

1.1 回调函数

假设我们有一个耗时的任务(例如文件下载),我们希望在任务完成时通知调用者。可以使用委托来实现回调。

using System;public delegate void DownloadCompletedHandler(string fileName);public class FileDownloader
{public DownloadCompletedHandler OnDownloadCompleted;public void DownloadFile(string fileName){Console.WriteLine($"Downloading {fileName}...");// 模拟下载过程System.Threading.Thread.Sleep(2000);Console.WriteLine($"{fileName} downloaded.");// 通知任务完成OnDownloadCompleted?.Invoke(fileName);}
}public class Program
{public static void Main(){FileDownloader downloader = new FileDownloader();// 绑定回调方法downloader.OnDownloadCompleted += fileName =>{Console.WriteLine($"Callback: {fileName} is ready to use.");};// 开始下载downloader.DownloadFile("example.txt");}
}

输出:

Downloading example.txt...
example.txt downloaded.
Callback: example.txt is ready to use.

1.2 多播委托

多播委托允许一个委托同时调用多个方法。以下是一个简单的日志记录系统的例子。

using System;public delegate void LogHandler(string message);public class Logger
{public LogHandler Log;public void RecordLog(string message){Console.WriteLine("Logging...");Log?.Invoke(message);}
}public class Program
{public static void Main(){Logger logger = new Logger();// 添加多个日志处理方法logger.Log += ConsoleLogger;logger.Log += FileLogger;// 记录日志logger.RecordLog("System started.");}public static void ConsoleLogger(string message){Console.WriteLine($"[Console] {message}");}public static void FileLogger(string message){// 模拟写入文件Console.WriteLine($"[File] {message}");}
}

输出:

Logging...
[Console] System started.
[File] System started.

2. 事件的实用例子

2.1 发布-订阅模式

事件通常用于实现发布-订阅模式。以下是一个简单的股票价格监控系统的例子。

using System;public class Stock
{// 定义事件public event EventHandler<PriceChangedEventArgs> PriceChanged;private decimal _price;public decimal Price{get => _price;set{if (_price != value){_price = value;// 触发事件PriceChanged?.Invoke(this, new PriceChangedEventArgs(_price));}}}
}// 自定义事件参数类
public class PriceChangedEventArgs : EventArgs
{public decimal NewPrice { get; }public PriceChangedEventArgs(decimal newPrice){NewPrice = newPrice;}
}public class Investor
{public string Name { get; }public Investor(string name){Name = name;}public void OnPriceChanged(object sender, PriceChangedEventArgs e){Console.WriteLine($"{Name} received notification: New price is {e.NewPrice:C}");}
}public class Program
{public static void Main(){Stock stock = new Stock();Investor investor1 = new Investor("Alice");Investor investor2 = new Investor("Bob");// 订阅事件stock.PriceChanged += investor1.OnPriceChanged;stock.PriceChanged += investor2.OnPriceChanged;// 修改股票价格stock.Price = 100.50m;stock.Price = 102.75m;}
}

输出:

Alice received notification: New price is $100.50
Bob received notification: New price is $100.50
Alice received notification: New price is $102.75
Bob received notification: New price is $102.75

2.2 异步事件处理

在某些情况下,事件处理可能需要异步执行。以下是一个简单的例子,模拟用户登录后触发异步通知。

using System;
using System.Threading.Tasks;public class UserLogin
{public event EventHandler<string> LoginSuccessful;public async Task LoginAsync(string username){Console.WriteLine($"Logging in as {username}...");await Task.Delay(2000); // 模拟登录延迟Console.WriteLine($"Login successful for {username}.");// 异步触发事件LoginSuccessful?.Invoke(this, username);}
}public class NotificationService
{public void OnLoginSuccessful(object sender, string username){Console.WriteLine($"Sending welcome email to {username}...");}
}public class Program
{public static async Task Main(){UserLogin userLogin = new UserLogin();NotificationService notifier = new NotificationService();// 订阅事件userLogin.LoginSuccessful += notifier.OnLoginSuccessful;// 模拟用户登录await userLogin.LoginAsync("JohnDoe");}
}

输出:

Logging in as JohnDoe...
Login successful for JohnDoe.
Sending welcome email to JohnDoe...

2.3 使用EventHandler<T>简化事件定义

C# 提供了泛型EventHandler<T>,可以简化事件的定义和使用。以下是一个按钮点击事件的例子。

using System;public class Button
{// 使用泛型EventHandler<T>public event EventHandler<ButtonClickEventArgs> Clicked;public void Click(){Console.WriteLine("Button clicked!");Clicked?.Invoke(this, new ButtonClickEventArgs(DateTime.Now));}
}// 自定义事件参数类
public class ButtonClickEventArgs : EventArgs
{public DateTime ClickTime { get; }public ButtonClickEventArgs(DateTime clickTime){ClickTime = clickTime;}
}public class Program
{public static void Main(){Button button = new Button();// 订阅事件button.Clicked += (sender, e) =>{Console.WriteLine($"Button was clicked at {e.ClickTime}");};// 模拟按钮点击button.Click();}
}

输出:

Button clicked!
Button was clicked at 10/10/2023 14:30:00

3. 总结

  • 委托适合用于封装方法引用,支持回调和多播。
  • 事件基于委托,主要用于实现发布-订阅模式,常见于GUI编程、异步任务和状态变化通知。
  • 上述例子涵盖了常见的应用场景,包括回调、日志记录、股票价格监控、异步事件处理和按钮点击事件。

通过这些例子,你可以更好地理解如何在实际项目中使用委托和事件来构建灵活且可扩展的系统。

与异步的结合

委托(Delegate)和事件(Event)与异步编程之间有着紧密的联系,尤其是在现代C#开发中。它们为异步编程提供了灵活且强大的机制,使得开发者能够以清晰、优雅的方式处理异步任务的结果或状态变化。

以下从几个方面详细分析它们之间的联系:


1. 委托与异步编程

1.1 回调机制

在异步编程中,一个常见的需求是:当某个异步操作完成时,通知调用者执行后续逻辑。委托可以作为回调函数的载体,用于封装需要在异步操作完成后执行的代码。

示例:使用委托处理异步任务结果
using System;
using System.Threading.Tasks;public delegate void TaskCompletedHandler(string result);public class AsyncTaskRunner
{public TaskCompletedHandler OnTaskCompleted;public async Task RunAsync(){Console.WriteLine("Starting asynchronous task...");string result = await Task.Delay(2000).ContinueWith(_ => "Task Result");// 异步任务完成后触发回调OnTaskCompleted?.Invoke(result);}
}public class Program
{public static async Task Main(){AsyncTaskRunner runner = new AsyncTaskRunner();// 绑定回调方法runner.OnTaskCompleted += result =>{Console.WriteLine($"Task completed with result: {result}");};// 启动异步任务await runner.RunAsync();}
}

输出:

Starting asynchronous task...
Task completed with result: Task Result

在这个例子中:

  • OnTaskCompleted 是一个委托,用于定义异步任务完成后的回调逻辑。
  • 当异步任务完成后,通过调用委托来通知调用者。

1.2 Func 和 Action

C# 提供了内置的泛型委托 Func<T>Action<T>,它们可以直接用于异步编程中的回调逻辑。

示例:使用 Func 处理异步任务
using System;
using System.Threading.Tasks;public class AsyncCalculator
{public async Task<int> CalculateAsync(Func<int, int, int> operation, int a, int b){Console.WriteLine("Calculating asynchronously...");await Task.Delay(1000); // 模拟耗时计算return operation(a, b);}
}public class Program
{public static async Task Main(){AsyncCalculator calculator = new AsyncCalculator();// 定义异步计算逻辑int result = await calculator.CalculateAsync((x, y) => x + y, 5, 10);Console.WriteLine($"Result: {result}");}
}

输出:

Calculating asynchronously...
Result: 15

在这个例子中:

  • Func<int, int, int> 是一个委托,表示接受两个整数参数并返回一个整数的方法。
  • 异步方法 CalculateAsync 使用该委托来封装具体的计算逻辑。

2. 事件与异步编程

2.1 事件驱动的异步通知

事件通常用于实现发布-订阅模式,在异步编程中,它可以用来通知订阅者某个异步操作的状态变化或完成情况。

示例:使用事件通知异步任务完成
using System;
using System.Threading.Tasks;public class AsyncTaskManager
{// 定义事件public event EventHandler<string> TaskCompleted;public async Task ExecuteAsync(){Console.WriteLine("Executing asynchronous task...");await Task.Delay(2000); // 模拟耗时操作// 触发事件TaskCompleted?.Invoke(this, "Task Completed Successfully");}
}public class Program
{public static async Task Main(){AsyncTaskManager manager = new AsyncTaskManager();// 订阅事件manager.TaskCompleted += (sender, message) =>{Console.WriteLine($"Notification: {message}");};// 执行异步任务await manager.ExecuteAsync();}
}

输出:

Executing asynchronous task...
Notification: Task Completed Successfully

在这个例子中:

  • TaskCompleted 是一个事件,用于通知订阅者异步任务已完成。
  • 事件的触发点是在异步操作完成后,确保调用者能够及时收到通知。

2.2 异步事件处理

在某些场景下,事件处理本身可能需要异步执行。C# 支持异步事件处理程序,可以通过 async voidasync Task 方法来实现。

示例:异步事件处理
using System;
using System.Threading.Tasks;public class NotificationService
{public event EventHandler<string> Notify;public async Task SendNotificationAsync(string message){Console.WriteLine("Sending notification...");await Task.Delay(1000); // 模拟异步发送// 触发事件Notify?.Invoke(this, message);}
}public class Program
{public static async Task Main(){NotificationService service = new NotificationService();// 订阅事件service.Notify += async (sender, message) =>{await Task.Delay(500); // 模拟异步处理Console.WriteLine($"Received notification: {message}");};// 发送通知await service.SendNotificationAsync("Hello, World!");}
}

输出:

Sending notification...
Received notification: Hello, World!

在这个例子中:

  • 事件处理程序是一个异步方法,使用 async 关键字定义。
  • 这种方式适用于需要在事件处理中执行耗时操作的场景。

3. 委托与事件在异步编程中的优势

3.1 解耦
  • 委托允许将异步任务的完成逻辑与任务本身解耦。例如,调用者可以自由定义回调逻辑,而无需修改异步方法的实现。
  • 事件进一步增强了这种解耦能力,因为它允许多个订阅者独立响应同一个异步操作的结果。
3.2 灵活性
  • 委托支持多播(Multicast),可以在异步任务完成后同时调用多个回调方法。
  • 事件通过发布-订阅模式,支持动态添加或移除订阅者,非常适合复杂的异步场景。
3.3 可读性
  • 使用委托和事件可以让异步代码更加清晰,尤其是当异步操作涉及多个步骤或状态变化时。

4. 实际应用场景

4.1 GUI 编程

在GUI框架(如WPF或WinForms)中,事件常用于处理用户交互(如按钮点击)。这些事件通常是异步触发的,因为用户操作可能发生在任意时刻。

4.2 Web API 调用

在调用远程API时,可以使用委托或事件来处理异步响应。例如:

  • 使用委托定义成功或失败的回调逻辑。
  • 使用事件通知订阅者API调用的结果。
4.3 数据流处理

在数据流处理(如Rx.NET或SignalR)中,事件常用于实时推送数据更新。这些数据更新通常是异步生成的。


5. 总结

  • 委托事件是异步编程的重要工具,它们通过回调机制和发布-订阅模式,帮助开发者优雅地处理异步任务的结果或状态变化。
  • 委托更适合简单的回调场景,而事件则适合复杂的多订阅者场景。
  • 在实际开发中,合理使用委托和事件,可以让异步代码更加清晰、灵活且易于维护。

希望这些内容能帮助你更好地理解委托、事件与异步编程之间的关系!

1. 原例子中的同步实现

1.1 文件下载回调(同步)
public class FileDownloader
{public DownloadCompletedHandler OnDownloadCompleted;public void DownloadFile(string fileName){Console.WriteLine($"Downloading {fileName}...");System.Threading.Thread.Sleep(2000); // 同步阻塞OnDownloadCompleted?.Invoke(fileName);}
}
  • 问题:使用 Thread.Sleep 模拟下载,会阻塞当前线程。
  • 改进:改用 Task.Delayasync/await 实现异步。

2. 异步版本的改写

2.1 异步文件下载(使用 async/await
using System;
using System.Threading.Tasks;public delegate void DownloadCompletedHandler(string fileName);public class FileDownloader
{public DownloadCompletedHandler OnDownloadCompleted;public async Task DownloadFileAsync(string fileName){Console.WriteLine($"Downloading {fileName}...");await Task.Delay(2000); // 异步等待,不阻塞线程Console.WriteLine($"{fileName} downloaded.");OnDownloadCompleted?.Invoke(fileName);}
}public class Program
{public static async Task Main(){FileDownloader downloader = new FileDownloader();downloader.OnDownloadCompleted += fileName =>{Console.WriteLine($"Callback: {fileName} is ready to use.");};await downloader.DownloadFileAsync("example.txt");}
}

输出:

Downloading example.txt...
example.txt downloaded.
Callback: example.txt is ready to use.

2.2 异步股票价格监控

原例子中的 Price 属性是同步的,但可以通过异步事件触发:

using System;
using System.Threading.Tasks;public class Stock
{public event EventHandler<PriceChangedEventArgs> PriceChanged;private decimal _price;public decimal Price{get => _price;set{if (_price != value){_price = value;PriceChanged?.Invoke(this, new PriceChangedEventArgs(_price));}}}// 异步模拟价格变化public async Task SimulatePriceChangeAsync(){for (int i = 0; i < 3; i++){await Task.Delay(1000); // 异步等待Price += 10.5m; // 触发PriceChanged事件}}
}public class Program
{public static async Task Main(){Stock stock = new Stock();stock.PriceChanged += (sender, e) =>{Console.WriteLine($"Price updated to {e.NewPrice:C}");};await stock.SimulatePriceChangeAsync();}
}

输出:

Price updated to $10.50
Price updated to $21.00
Price updated to $31.50

3. 原例子中的潜在异步场景

3.1 事件处理本身是异步的

即使事件触发是同步的,事件处理方法也可以是异步的。例如:

public class NotificationService
{public event EventHandler<string> Notify;public void SendNotification(string message){Console.WriteLine("Sending notification...");Notify?.Invoke(this, message);}
}public class Program
{public static void Main(){NotificationService service = new NotificationService();// 异步事件处理service.Notify += async (sender, message) =>{await Task.Delay(500); // 模拟异步操作Console.WriteLine($"Received: {message}");};service.SendNotification("Hello, World!");Console.ReadLine(); // 防止主线程退出}
}

输出:

Sending notification...
(等待500ms后)
Received: Hello, World!

4. 总结

  • 原例子中的委托和事件是同步的,但可以通过以下方式实现异步:
    1. 使用 async/awaitTask 改写耗时操作。
    2. 在事件处理方法中使用异步逻辑(async voidasync Task)。
  • 异步的优势
    • 避免阻塞主线程(例如在GUI应用中保持界面响应)。
    • 提高资源利用率(例如在服务器端处理高并发请求)。

通过结合委托、事件和异步编程,可以构建高效且可维护的异步系统。

相关文章:

C# | 委托 | 事件 | 异步

委托&#xff08;Delegate&#xff09;和事件&#xff08;Event&#xff09; 在C#和C中&#xff0c;委托&#xff08;Delegate&#xff09;与事件&#xff08;Event&#xff09;以及函数对象&#xff08;Function Object&#xff09;是实现回调机制或传递行为的重要工具。虽然…...

数据结构——顺序表与链表

1. 基础介绍 1、线性结构&#xff1a; 如果一个数据元素序列满足&#xff1a; &#xff08;1&#xff09;除第一个和最后一个数据元素外&#xff0c;每个数据元素只有一个前驱数据元素和一个后继数据元素&#xff1b; &#xff08;2&#xff09;第一个数据元素没有前驱数据…...

【uniapp】图片添加canvas水印

目录 需求&背景实现地理位置添加水印 ios补充 需求&背景 需求&#xff1a;拍照后给图片添加水印, 水印包含经纬度、用户信息、公司logo等信息。 效果图&#xff1a; 方案&#xff1a;使用canvas添加水印。 具体实现&#xff1a;上传图片组件是项目里现有的&#xff…...

ElementUI 级联选择器el-cascader启用选择任意一级选项,选中后关闭下拉框

1、启用选择任意一级选项 在 el-cascader 标签上加上配置项&#xff1a; :props"{ checkStrictly: true }"例如&#xff1a; <el-cascaderref"selectedArrRef"v-model"selectedArr":options"optionsList":props"{ checkStri…...

【音视频】ffplay常用命令

一、 ffplay常用命令 -x width&#xff1a;强制显示宽度-y height&#xff1a;强制显示高度 强制以 640*360的宽高显示 ffplay 2.mp4 -x 640 -y 360 效果如下 -fs 全屏显示 ffplay -fs 2.mp4效果如下&#xff1a; -an 禁用音频&#xff08;不播放声音&#xff09;-vn 禁…...

5人3小时复刻Manus?开源OpenManus项目全解剖,我的DeepSeek股票报告这样诞生

大家好,我是大 F,深耕AI算法十余年,互联网大厂技术岗。分享AI算法干货、技术心得。 更多文章可关注《大模型理论和实战》、《DeepSeek技术解析和实战》,一起探索技术的无限可能! OpenManus是什么 1. 项目背景 OpenManus 是由 MetaGPT 核心团队仅用 3 小时复刻而成的开源…...

【Python运维】用Python自动化AWS资源管理:利用boto3实现高效管理S3桶和EC2实例

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着云计算的普及,AWS(Amazon Web Services)已经成为许多企业和开发者首选的云平台。为了提高工作效率,自动化管理AWS资源成为了一个热…...

django各种mixin用法

在 Django 中,Mixin 是一种用于扩展类功能的设计模式。通过 Mixin,可以在不修改原有类的情况下,为其添加新的方法或属性。Django 中的 Mixin 广泛应用于视图(View)、表单(Form)、模型(Model)等组件中。以下是 Django 中常见 Mixin 的用法和示例: 一、视图(View)中的…...

Java 大视界 -- Java 大数据在智能教育考试评估与学情分析中的应用(112)

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

Manus AI : Agent 元年开启.pdf

Manus AI : Agent 元年开启.pdf 是由华泰证券出品的一份调研报告&#xff0c;共计23页。报告详细介绍了Manus AI 及 Agent&#xff0c;主要包括Manus AI 的功能、优势、技术能力&#xff0c;Agent 的概念、架构、应用场景&#xff0c;以及 AI Agent 的类型和相关案例&#xff0…...

【计算机网络】计算机网络的性能指标——时延、时延带宽积、往返时延、信道利用率

计算机网络的性能指标 导读 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;&#xff01; 在上一篇内容中我们介绍了计算机网络的三个性能指标——速率、带宽和吞吐量。用大白话来说就是&#xff1a;网速、最高网速和实时网速。 相信大家看到这三个词应该就…...

FreeRTOS第15篇:FreeRTOS链表实现细节03_List_t与ListItem_t的奥秘

文/指尖动听知识库-星愿 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:深入FreeRTOS内核:从原理到实战的嵌入式开发指南 1 FreeRTOS列表的核心数据结构 FreeRTOS的列表实现由两个关键结构体组成:List_t(列表)和ListItem_t(列表项)。它们共同…...

git 添加额外的远程仓库 URL

要使用 git branch -a 查看 net-next 远程仓库中的所有分支&#xff0c;请按照以下步骤操作&#xff1a; 步骤 1: 确保已添加 net-next 远程仓库 如果尚未添加 net-next 远程仓库&#xff0c;请运行以下命令&#xff1a; git remote add net-next git://git.kernel.org/pub/s…...

不同类型光谱相机的技术差异比较

一、波段数量与连续性 ‌多光谱相机‌ 波段数&#xff1a;通常4-9个离散波段&#xff0c;光谱范围集中于400-1000nm‌。 数据特征&#xff1a;光谱呈阶梯状&#xff0c;无法连续覆盖&#xff0c;适用于中等精度需求场景&#xff08;如植被分类&#xff09;‌。 ‌高光谱相机…...

Swift系列01-Swift语言基本原理与设计哲学

本文将深入探讨Swift的核心原理、设计理念以及与Objective-C的对比 1. Swift与Objective-C的架构差异分析 Swift和Objective-C尽管可以无缝协作&#xff0c;但它们的架构设计存在本质差异。 1.1语言范式 Objective-C是一种动态语言&#xff0c;建立在C语言之上并添加了Smal…...

《OpenCV》——dlib(人脸应用实例)

文章目录 dlib库dlib库——人脸应用实例——表情识别dlib库——人脸应用实例——疲劳检测 dlib库 dlib库的基础用法介绍可以参考这篇文章&#xff1a;https://blog.csdn.net/lou0720/article/details/145968062?spm1011.2415.3001.5331&#xff0c;故此这篇文章只介绍dlib的人…...

以太网通讯

接口开发笔记-WebApi-CSDN博客 以太网常用通讯协议 1、modbus tcp using EasyModbus; using System;class Program {static void Main(string[] args){// 创建Modbus客户端实例ModbusClient modbusClient new ModbusClient("192.168.1.100"); // IP地址modbusCli…...

UDP学习笔记(一)为什么UDP需要先将数据转换为字节数组

UDP 发送数据时需要先将数据转换为字节数组再发送&#xff0c;主要是因为计算机网络传输的最基本单位是“字节”&#xff08;Byte&#xff09;。让我们从以下几个方面来深入理解这个设计选择&#xff1a; 1. 计算机网络只能传输“字节” 在网络通信中&#xff0c;无论是 TCP 还…...

数据分析/数据科学常见SQL题目:连续登录用户、留存率、最大观看人数

文章目录 1. SQL的执行顺序是什么&#xff1f;on和join谁先执行&#xff0c;为什么&#xff1f;on和where的区别&#xff1f;2. 已知表user,字段id, date&#xff0c;求新用户的次日留存率3. 已知表user&#xff0c;字段id&#xff0c;date&#xff0c;求每个日期新用户的次日留…...

【Conda】Windows安装conda/Anaconda环境

安装conda并配置powershell 访问该网址&#xff0c;下载安装即可&#xff1a; Anaconda下载 安装完成后&#xff0c;打开Anaconda&#xff0c;并访问Powershell Prompt 弹出Windows Terminal&#xff0c;并正常进入Conda 【非必须】如果不是通过Windows Terminal打开&#x…...

olmOCR:高效精准的 PDF 文本提取工具

在日常的工作和学习中&#xff0c;是否经常被 PDF 文本提取问题困扰&#xff1f;例如&#xff1a; 想从学术论文 PDF 中提取关键信息&#xff0c;却发现传统 OCR 工具识别不准确或文本格式混乱&#xff1f;需要快速提取商务合同 PDF 中的条款内容&#xff0c;却因工具不给力而…...

数字投屏叫号器-发射端python窗口定制

窗口 本系列前章介绍&#xff0c;叫号器的显示端&#xff0c;完成了视频音频的形成和传输的介绍。本章节开始定制小窗口。 最终实现&#xff0c;处于桌面最前端&#xff0c;发送指令&#xff0c;集合前篇即可完成&#xff1a; 处理本地text.txt更新&#xff0c;随之被rtsp采集…...

从零开始实现大语言模型(十四):高阶训练技巧

1. 前言 预训练大语言模型的流程与训练普通神经深度网络模型本质上并没有任何不同。可以使用深度学习实践中已经被证明非常有效的高阶训练技巧&#xff0c;优化大语言模型预训练流程&#xff0c;使大语言模型预训练效率更高&#xff0c;训练过程更稳定。 本文介绍深度学习领域…...

Spring-framework源码编译

版本统一&#xff08;搭配其他版本会遇到不可知错误&#xff09;&#xff1a; 1&#xff09;spring 5.2.X&#xff08;5.5.26&#xff09; 2&#xff09;JDK8 3&#xff09;Gradle:5.6.4 可以在gradle-wrapper.properties中修改 https\://services.gradle.org/distribution…...

分布式系统的核心挑战与解决方案

1、分布式系统的引入 在移动互联网、云计算和物联网的推动下&#xff0c;现代软件系统需要处理亿级用户请求、PB级数据存储和毫秒级响应需求。传统的单体架构受限于单机性能瓶颈和容灾能力&#xff0c;逐渐被分布式系统取代。例如&#xff0c;电商平台在“双十一”期间需应对每…...

fastjson漏洞

fastjson漏洞 fastjson工作原理攻击原理补充 例子 fastjson工作原理 fastjson的作用是将JAVA对象转换成对应的json表示形式&#xff0c;也可以反过来将json转化为对应的Java对象。fastjson使用AutoType功能进行反序列化&#xff0c;AutoType使用type标记字符的原始类型&#x…...

upload-labs详解(13-20)文件上传分析

目录 upload-labs-env upload-labs-env第十三关 文件包含漏洞 代码 测试 上传一个.jpg图片 上传一个.png文件 上传一个.gif图片 upload-labs-env第十四关 代码 思路 upload-labs-env第十五关 代码 思路 upload-labs-env第十六关 代码 思路 测试 上传gif格式…...

HTML第四节

一.复合选择器 1.后代选择器 注&#xff1a;1.后代选择器会选中后代所有的要选择的标签 2.儿子选择器 3.并集选择器 注&#xff1a;1.注意换行&#xff0c;同时选中多种标签 4.交集选择器 注&#xff1a;1.标签选择器放在最前面&#xff0c;例如放在类选择器的前面 2.两个选择…...

基于 LeNet 网络的 MNIST 数据集图像分类

1.LeNet的原始实验数据集MNIST 名称&#xff1a;MNIST手写数字数据集 数据类型&#xff1a;灰度图 &#xff08;一通道&#xff09; 图像大小&#xff1a;28*28 类别数&#xff1a;10类&#xff08;数字0-9&#xff09; 1.通过torchvision.datasets.MNIST下载并保存到本地…...

win11编译llama_cpp_python cuda128 RTX30/40/50版本

Geforce 50xx系显卡最低支持cuda128&#xff0c;llama_cpp_python官方源只有cpu版本&#xff0c;没有cuda版本&#xff0c;所以自己基于0.3.5版本源码编译一个RTX 30xx/40xx/50xx版本。 1. 前置条件 1. 访问https://developer.download.nvidia.cn/compute/cuda/12.8.0/local_…...