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

跨线程GCHandle,如何使用PinnedIntArray等结构来管理内存,以及如何确保在处理完成后释放资源。

在C#中,内存管理由垃圾回收器(Garbage Collector, GC)自动处理,这是语言和.NET框架的重要特性之一。然而,在某些情况下,特别是与非托管代码(如C/C++)交互时,开发者需要对内存进行手动管理。PinnedIntArray并不是一个内置的结构或类,而是一个假设的概念用于说明如何固定数组的内存位置,以便和非托管代码(如C/C++)交互。固定内存的位置可以防止垃圾回收过程移动对象,从而确保安全的指针操作。

历史背景

C#诞生于2000年,作为微软.NET框架的一部分,与Java类似,C#采用了垃圾回收机制,极大地减少了内存泄漏和指针错误的风险。然而,随着C#广泛用于系统级编程和跨平台开发,与非托管代码的交互变得越来越常见。这就需要有一种方法在特定场景下手动管理内存,与非托管资源进行交互。

固定内存(Pinned Memory)

在与非托管代码交互的时候,常常需要使用P/Invoke或C++/CLI等技术调用非托管函数。非托管函数期望接收指向固定内存的指针,而垃圾回收器可能会在后台移动对象,使其内存地址无效。因此,C#引入了固定内存的功能。

  • fixed 语句

最早引入的固定内存机制是fixed语句,它可以通过直接获取托管对象的内存地址来阻止垃圾收集器移动该对象。

int[] array = new int[5] { 1, 2, 3, 4, 5 };
unsafe
{
    fixed (int* ptr = array)
    {
        // 使用ptr进行非托管代码交互
    }
}
// 当离开fixed语句块后,object会被解除固定
  • GCHandle 结构

随着语言的发展,C#提供了GCHandle结构来提供更灵活的内存管理。这尤其适用于需要长时间固定对象的场景。

使用GCHandle固定内存

GCHandle是用于获取对象的非托管指针的结构。你可以使用它来固定对象的位置,以便与非托管代码交互。

示例代码:

using System;
using System.Runtime.InteropServices;

class PinnedIntArrayExample
{
    public static void Main()
    {
        // 创建一个托管整数数组
        int[] managedArray = new int[5] { 1, 2, 3, 4, 5 };
        
        // 声明一个GCHandle,用于固定数组
        GCHandle handle = default;

        try
        {
            // 分配一个GCHandle以固定数组,这样垃圾收集器就无法移动它
            handle = GCHandle.Alloc(managedArray, GCHandleType.Pinned);
            
            // 获取数组的指针
            IntPtr pointer = handle.AddrOfPinnedObject();
            
            // 输出指针的值(仅供演示)
            Console.WriteLine("Pointer address: " + pointer);

            // 在这个点,你可以通过非托管代码使用这个指针
            // 假设调用一些外部方法,如:ExternalMethod(pointer);

        }
        finally
        {
            // 释放句柄,使垃圾回收器能够再次移动和回收对象
            if (handle.IsAllocated)
            {
                handle.Free();
            }
        }
    }
}

关键步骤说明:

  1. 分配GCHandle:
    使用GCHandle.Alloc方法来固定数组,可以选择GCHandleType.Pinned以确保对象不被垃圾回收器移动。
  2. 获取内存地址:
    使用AddrOfPinnedObject()方法获取数组的指针地址,以便传递给需要非托管内存地址的外部函数。
  3. 释放GCHandle:
    在finally块中使用handle.Free()来释放固定,从而允许垃圾收集器正常操作。
    确保在所有操作完成后释放GCHandle,以防止内存泄漏。

注意事项:

  • 异常处理:始终在try…finally结构中固定对象,以确保即使发生异常也能够正确释放资源。
  • 性能考量:只有在需要的情况下才固定内存,因为它会关闭垃圾收集的一些优化(如对象移动)。
  • 边界条件:在使用指针时,要特别注意数组边界条件,以免产生访问冲突或未定义的行为。
  • Interop调用:在使用非托管代码时,请确保调用被正确定义并小心使用,以避免内存损坏和崩溃。

为什么使用GCHandle?

  • 灵活性:与简单的fixed语句相比,GCHandle能跨多个方法和线程保持对象固定。
  • 控制生命周期:开发者完全掌控何时释放句柄,有助于预防内存泄漏。
  • 更细粒度的内存管理:适用于复杂对象交互和较长固定需求。

资源释放

使用固定内存时,开发者必须保证:

  • 句柄释放:确保调用handle.Free()解除固定。
  • 防止内存泄漏:在所有可能情况下释放资源,通常使用try…finally块。
  • 正确使用指针:被固定的内存的指针运算中,防止越界访问。

通过理解和使用这些技术,开发者可以更安全和高效地管理C#中的内存,特别是在需要与非托管代码交互的场景中。从历史上看,C#通过这些特性满足了从高效应用到系统级编程的多种需求。


http://www.kler.cn/a/387168.html

相关文章:

  • [项目代码] YOLOv5 铁路工人安全帽安全背心识别 [目标检测]
  • HarmonyOS Next 实战卡片开发 02
  • Iceberg 写入和更新模式,COW,MOR(Copy-on-Write,Merge-on-Read)
  • 前端入门一之ES6--面向对象、够着函数和原型、继承、ES5新增方法、函数进阶、严格模式、高阶函数、闭包
  • YOLOv11融合ICCV[2023]动态蛇形卷积Dynamic模块及相关改进思路|YOLO改进最简教程
  • java---认识异常(详解)
  • 软件知识:什么是软件工程?
  • 【前端】Svelte:组件封装与使用
  • 校园周边美食探索及分享平台
  • C#里使用预定义函数来对数组进行翻转
  • 美格智能5G车规级通信模组: 5G+C-V2X连接汽车通信未来十年
  • [产品管理-60]:产品的情感化设计与常用工具:感性工学、情感分析、神经网络法、微软反应卡、突发情绪法
  • jmeter结合ansible分布式压测--1数据准备
  • 使用docker形式部署jumpserver
  • oracle-函数-grouping sets(x1,x2,x3...)的妙用
  • Go语言开发商城管理后台-GoFly框架商城插件已发布 需要Go开发商城的朋友可以来看看哦!
  • 【wpf】ResourceDictionary 字典资源的用法
  • 系统架构师2023版:习题
  • 七大AI知识库工具概览
  • 搜维尔科技:【煤矿虚拟仿真】煤矿企业、高校、科研单位-多语言支持、数字孪生、交互式学习体验
  • Windows10 下通过 Visual Studio2022 编译 openssl 3.4
  • go语言使用总结(持续更新)
  • 前端开发利器:npm 软链接
  • MYSQL-显示错误信息ERRORS 的语句(十七)
  • 软件项目整体实施方案,从立项到交付验收完整过程实施方案,软件实施方案(word原件)
  • [241110] 微软发布多智能体系统Magentic-One | 社区讨论:Ubuntu 26.04 LTS 发布前移除 Qt 5