# 运行管理资源申请与释放

开发应用时，应用程序中必须包含运行管理资源申请的代码逻辑，关于运行管理资源申请的接口调用流程，请先参见AscendCL接口调用流程了解整体流程，再查看本节中的资源申请、释放流程说明。

#### 基本原理

您需要按顺序依次申请如下运行管理资源：Device、Context、Stream，确保可以使用这些资源执行运算、管理任务。所有数据处理都结束后，需要按顺序依次释放运行管理资源：Stream、Context、Device。

您需要按照Device、Context、Stream的顺序依次申请。其中，创建Context、Stream的方式分为隐式创建和显式创建，其适用场景有所不同：

- 隐式创建Context和Stream：适合简单、无复杂交互逻辑的应用，但缺点在于，在多线程编程中，每个线程都使用默认Context或默认Stream，默认Stream中任务的执行顺序取决于操作系统线程调度的顺序。
- 显式创建Context和Stream：推荐显式，适合大型、复杂交互逻辑的应用，且便于提高程序的可读性、可维护性。

- 关于单进程、单线程、单Stream场景如下所示：

  - 单进程：一个应用程序对应一个进程。
  - 单线程：不创建多个线程时，默认只有一个线程。
  - 单Stream：整个开发的过程中使用同一个Stream。
    对于同一个Stream中的异步任务，AscendCL会按照应用程序中任务的顺序执行任务，确保异步任务执行的顺序。

- 关于多线程、多Stream的场景请参见  Stream管理
。


#### 运行管理资源申请流程

图1 运行管理资源申请流程

关键接口的说明如下：

1. 申请运行管理资源时，需按顺序依次申请：Device、Context、Stream。

  - 调用aclrtSetDevice接口显式指定用于运算的Device。

    - 调用      aclrtCreateContext
接口显式创建Context，调用      aclrtCreateStream
接口显式创建Stream。
    - 如果不显式创建Context和Stream，您可以使用aclrtSetDevice接口隐式创建的默认Context和默认Stream，但默认Context和默认Stream存在如下限制：

      - 一个Device对应一个默认Context，默认Context不能通过        aclrtDestroyContext
接口来释放。
      - 一个Device对应一个默认Stream，默认Stream不能通过        aclrtDestroyStream
接口来释放。默认Stream作为接口入参时，直接传NULL。
      - 默认Context、默认Stream，是在调用        aclrtResetDevice
接口后自动释放。
  - 隐式指定用于运算的Device。
    调用aclrtCreateContext接口显式创建Context，调用aclrtCreateStream接口显式创建Stream。系统在显式创建Context时，系统内部会调用aclrtSetDevice接口指定运行的Device，Device ID通过aclrtCreateContext接口传入。

2. （可选）调用  aclrtGetRunMode
接口获取软件栈的运行模式，根据运行模式来判断后续的内存申请接口调用逻辑。
  如果查询结果为ACL_HOST，则数据传输时涉及申请Host上的内存。

  如果查询结果为ACL_DEVICE，则数据传输时仅需申请Device上的内存。

  数据传输的详细介绍请参见数据传输。


#### 运行管理资源释放流程

图2 运行管理资源释放流程

关键接口的说明如下：

- 释放运行管理资源时，需按顺序依次释放：Stream、Context、Device。
- 显式创建Context和Stream时，需调用  aclrtDestroyStream
接口释放Stream，再调用  aclrtDestroyContext
接口释放Context。若显式调用  aclrtSetDevice
接口指定运算的Device时，还需调用  aclrtResetDevice
接口释放Device上的资源。
- 不显式创建Context和Stream时，仅需调用  aclrtResetDevice
接口释放Device上的资源。


#### 示例代码

您可以从样例介绍中获取完整样例代码。

调用接口后，需增加异常处理的分支，并记录报错日志、提示日志，此处不一一列举。以下是关键步骤的代码示例，不可以直接拷贝编译运行，仅供参考。

```
// 初始化变量
extern bool g_isDevice;

// =====运行管理资源申请=====
// 指定运算的Device
aclError ret = aclrtSetDevice(deviceId_);

// 显式创建一个Context，用于管理Stream对象
ret = aclrtCreateContext(&context_, deviceId_);

// 显式创建一个Stream
// 用于维护一些异步操作的执行顺序，确保按照应用程序中的代码调用顺序执行任务
ret = aclrtCreateStream(&stream_);

// 获取当前昇腾AI软件栈的运行模式，根据不同的运行模式，后续的接口调用方式不同

aclrtRunMode runMode;
ret = aclrtGetRunMode(&runMode);
g_isDevice = (runMode == ACL_DEVICE);
// =====运行管理资源申请=====

// ......

// =====运行管理资源释放=====
ret = aclrtDestroyStream(stream_);
ret = aclrtDestroyContext(context_);
ret = aclrtResetDevice(deviceId_);
// =====运行管理资源释放=====

// ......
```
