昇腾社区首页
中文
注册

CreateTimer

基本原理

创建Timer的接口调用流程如图1所示。

图1 创建Timer的接口调用流程
  1. 资源初始化。

    用户在调用OpenHiva接口之前,需要先调用OpenHiva::Init接口进行初始化,返回值为0代表初始化成功,否则失败。

    初始化动作主要包含定时器节点名注册、线程组创建、资源申请等动作。
    • 节点名注册:
      • 不同节点的节点名称不能重复,否则会导致后面启动的节点初始化失败。
      • 如果初始化失败,节点不能正常执行发布或者订阅动作。
    • 线程组创建:

      调用OpenHiva::Init接口时需传入线程组参数,请提前按需创建线程组(ScheduleGroup)。每个线程组中存放回调函数的若干信息,包括线程组名字(groupName)、线程组调度类型(scheduleType)等。

      当线程组中ThreadGroup.scheduleType取值不同,OpenHiva内部的处理流程也不同:

      • UNBIND_AICPU(推荐):表示非确定域线程,线程组不绑核。每个组会启动一个工作线程,等待事件激活。工作线程具体在哪个CPU上运行由操作系统进行调度。
      • BIND_AICPU: 表示确定域线程,线程组绑核。每个组启动的工作线程数最大为4(具体由CPU核数决定),并绑在各个核上,每个核绑一个。其调度状态由事件调度机制来确定。这种机制实时性高,在很大程度上避免了由操作系统内核调度引起的时间抖动,保证了线程从休眠到运行状态切换的“确定性”。
  2. 创建Timer。
    1. 在创建定时器之前,必须先注册节点,即创建节点句柄OpenHiva::Node n。
    2. 再使用创建的节点句柄n调用Hiva::CreateTimer接口创建Timer,调用完成后返回Hiva::Timer对象。

      调用CreateTimer接口时,入参groupName必须和OpenHiva::Init接口中设置的groupName相同。此外,入参oneshot和autostart的配置,会影响Timer的可触发次数和启动方式。

      • oneshot:影响Timer的可触发次数:
        • True:表示定时器采用一次性的oneshot模式。在进程运行时间内,定时器只会被触发一次。
        • False(默认值):表示定时器采用周期性的periodic模式。在进程运行时间内,定时器会被周期性触发,直到进程资源被清理。
      • autostart:影响Timer的启动方式:
        • True(默认值):表示自动启动。在CreateTimer接口返回时,定时器便已经开启。
        • False:表示不自动启动。在CreateTimer接口返回时,定时器未启动,需通过Hiva::Timer::Start接口手动开启。
    3. 后续可通过Hiva::Timer对象启动/停止定时器(Hiva::Timer::Start/Hiva::Timer::Stop)、设置定时器触发周期(Hiva::Timer::SetPeriod)等。

      定时器触发周期是触发回调函数的周期,当定时器时间到期后,会执行用户的回调函数。

  3. 资源释放。

    进程结束前,调用OpenHiva::Shutdown接口进行资源清理。资源释放后,定义的OpenHiva接口将无法使用。

示例代码

创建Timer的关键步骤代码示例如下,仅供参考:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
#include <iostream>
#include <string>
#include <memory>
#include <unistd.h>
#include <sstream>
#include "open/init.h"
#include "open/node.h"
#include "open/service_client.h"
#include "open/service_options.h"
#include "std_msgs/include/StringMessage.h"

static constexpr uint32_t MAX_NAME_SIZE = 100;
static constexpr uint32_t CALL_TIMEOUT = 1000;                      // 单位ms
Hiva::StdMsgs::StringMessage req;
Hiva::StdMsgs::StringMessage res;

void Usage()
{
    std::cout << "Usage: pmupload test_create_client nodeName topicName groupName bindType reqSize resSize blockNum msgCnt" << std::endl;
    std::cout << "if argc==2, client will use default value: pmupload test_create_client testNodeClient testService testGroupClient 0 100 100 10 10" << std::endl;
    std::cout << "bindType can be 0 1 2" << std::endl;
    std::cout << "Pointing: req/res size must bigger than messageSize + 8, or serialize will fail" << std::endl;
}

int main(int argc, char **argv)
{
    // 1. 资源初始化
    std::string nodeName = "testNodeClient";
    std::string groupName = "testGroupClient";
    std::string serviceName = "testService";
    OpenHiva::ScheduleType scheType = OpenHiva::ScheduleType(0);
    int32_t reqDataSize = 100;              // default size
    int32_t resDataSize = 100;             // default size
    int32_t blockNum = 10;                 // default size
    uint32_t callTimes = 10U;
    int argNum = 9;
    if (argc < argNum) {
        Usage();
        if (argc != 2) {
            return 0;
        }
    } else {
        nodeName = argv[1];
        serviceName = argv[2];
        groupName = argv[3];
        scheType = (OpenHiva::ScheduleType)strtol(argv[4], NULL, 10);
        reqDataSize = (int32_t)strtol(argv[5], NULL, 10);
        resDataSize = (int32_t)strtol(argv[6], NULL, 10);
        blockNum = (int32_t)strtol(argv[7], NULL, 10);
        callTimes = (uint32_t)strtol(argv[8], NULL, 10);
    }
    // 定义线程组
    std::vector<OpenHiva::ScheduleGroup> scheGrpVec;
    OpenHiva::ScheduleGroup scheGrp;
    scheGrp.groupName = groupName;
    scheGrp.scheduleType = scheType;
    scheGrpVec.push_back(scheGrp);
    // 调用资源初始化接口
    OpenHiva::Init(argc, argv, scheGrpVec);
    HIVA_EVENT("service client init ok!");
    HIVA_INFO("reqDataSize=%d.", reqDataSize);
    HIVA_INFO("resDataSize=%d.", resDataSize);
    HIVA_INFO("blockNum=%d.", blockNum);

    // 2. 创建客户端
    // 构造Node对象
    OpenHiva::Node node(nodeName);
    // 构造ServiceOptions  
    OpenHiva::ServiceOptions serOps;
    serOps.BuildShmOptions(reqDataSize, resDataSize, blockNum);
    // 通过NodeHandle对象调用CreateClient接口创建ServiceClient对象
    std::shared_ptr<OpenHiva::ServiceClient> client = node.CreateClient(serviceName, serOps);
    // 当构造ServiceClient对象失败时,调用Shutdown函数释放资源并退出
    if (client == nullptr) {
        HIVA_ERROR("create client failed");
        OpenHiva::Shutdown();
        return 0;
    }

    // 3. 发送请求并接收响应
    uint32_t timeout = CALL_TIMEOUT;
    // 判断Hiva节点状态。当节点是使能状态,返回true;当节点是shutdown或初始化失败状态,返回false,收发包均不能正确进行
    for (uint32_t i = 0; i < callTimes && OpenHiva::Ready(); ++i) {
        std::stringstream ss;
        ss << "request: ==>Hello World " << i;                  // its size is 25 + i.size;
        req.stringData = ss.str();
        HIVA_WARN("%s", req.stringData.c_str());
        timespec beginTime;
        clock_gettime(CLOCK_MONOTONIC, &beginTime);
        uint32_t ret = client->InvokeService(req, res, timeout);
        HIVA_INFO("ans: [%s] ret[%d] ", res.stringData.c_str(), ret);
        if (ret == 0U) {
            timespec endTime;
            clock_gettime(CLOCK_MONOTONIC, &endTime);
            uint32_t useTime = ((endTime.tv_sec - beginTime.tv_sec) * 1000000) +
                               ((endTime.tv_nsec - beginTime.tv_nsec) / 1000);
            HIVA_INFO("InvokeService res_ans=%s, ret=%d, useTime=%u us",
                res.stringData.c_str(), ret, useTime);
        }
        sleep(1);
    }

    // 4. 资源释放
    client->Destroy();
    OpenHiva::Shutdown();
    return 0;
}