当前位置:首页 > 技术文章 > 正文内容

基于OpenHarmony标准系统的C++公共基础类库案例:Semaphore

zonemu2个月前 (07-25)技术文章22

1、程序简介

该程序是基于OpenHarmony标准系统的C++公共基础类库的线程处理:Sempahore。

本案例完成如下工作:

(1)无名信号量使用方法

  • 定义1个无名信号量,1个供无名信号量管理的公共资源变量;
  • 创建5个线程,每个线程做5次for循环,for循环的内容是获取无名信号量,并修改公共资源变量;

(2)有名信号量使用方法

  • 定义1个有名信号量,1个供有名信号量管理的公共资源变量;
  • 创建1个线程A,通过Open获取信号量,做5次for循环,for循环的内容是通过Wait获取有名信号量,如果获取成功则修改公共资源变量(即累加1),最后释放信号量;
  • 创建1个线程B,通过Open获取信号量,做5次for循环,for循环的内容是通过TryWait获取有名信号量,如果获取成功则修改公共资源变量(即累加10),最后释放信号量;
  • 创建1个线程C,通过Open获取信号量,做5次for循环,for循环的内容是通过TimedWait获取有名信号量,如果获取成功则修改公共资源变量(即累加100),最后释放信号量;

2、基础知识

C++公共基础类库为标准系统提供了一些常用的C++开发工具类,包括:

  • 文件、路径、字符串相关操作的能力增强接口
  • 读写锁、信号量、定时器、线程增强及线程池等接口
  • 安全数据容器、数据序列化等接口
  • 各子系统的错误码相关定义

2.1、添加C++公共基础类库依赖

修改需调用模块的BUILD.gn,在external_deps或deps中添加如下:

ohos_shared_library("xxxxx") {
  ...
  external_deps = [
    ...
    # 动态库依赖(可选)
    "c_utils:utils",
    # 静态库依赖(可选)
    "c_utils:utilsbase",
    # Rust动态库依赖(可选)
    "c_utils:utils_rust",
  ]
  ...
}

一般而言,我们只需要填写"c_utils:utils"即可。

2.2、Semaphore头文件

C++公共基础类库的Semaphore头文件在:
//commonlibrary/c_utils/base/include/semaphore_ex.h

可在源代码中添加如下:

#include <semaphore_ex.h>

OpenHarmony信号量根据种类可以分为有名信号量和无名信号量,所以命令空间如下:

(1)无名信号量命名空间

OHOS::Semaphore

(2)有名信号量命名空间

OHOS::NamedSemaphore

2.3、OHOS::Samaphore接口说明

Semaphore为无名信号量。

2.3.1、Samaphore

构造函数, 构造一个Samaphore对象。

Semaphore(int value = 1);

参数说明:

参数名称

类型

参数说明

value

int

信号量当前资源数量

2.3.2、~Semaphore

析构函数。

~Semaphore();

2.3.3、Wait

等待/获取信号量(即信号量 -1)。

void Wait();

2.3.4、Post

释放信号量(即信号量 +1)。

void Post();

2.4、OHOS::NamedSemaphore接口说明

NamedSemaphore为有名信号量。

2.4.1、NamedSemaphore

构造函数, 构造NamedSemaphore对象。

NamedSemaphore(size_t size)
NamedSemaphore(const std::string& name, size_t size)

参数说明:

参数名称

类型

参数说明

name

std::string

信号量名称

size

size_t

信号量有效资源数量

2.4.2、~NamedSemaphore

析构函数。

~NamedSemaphore();

2.4.3、Create

创建并初始化有名信号量。

bool Create();

返回值说明:

类型

返回值说明

bool

true表示成功,false表示失败

2.4.4、Unlink

将有名信号量文件从系统中删除。

bool Unlink();

返回值说明:

类型

返回值说明

bool

true表示成功,false表示失败

2.4.5、Open

打开一个已经创建的有名信号量文件。

bool Open();

返回值说明:

类型

返回值说明

bool

true表示成功,false表示失败

2.4.6、Close

关闭有名信号量。

bool Close();

返回值说明:

类型

返回值说明

bool

true表示成功,false表示失败

2.4.7、Wait

等待/获取信号量(信号量 -1)。

bool Wait();

返回值说明:

类型

返回值说明

bool

true表示成功,false表示失败

2.4.8、TryWait

等待/获取信号量(信号量 -1)的接口;非阻塞版。

bool TryWait();

返回值说明:

类型

返回值说明

bool

true表示成功,false表示失败

2.4.9、TimedWait

等待/获取信号量(信号量 -1);指定阻塞时间版。

bool TimedWait(const struct timespec& ts);

参数说明:

参数名称

类型

参数说明

ts

struct timespec

绝对时间。注意:ts是utc时间,不是相对时间。

返回值说明:

类型

返回值说明

bool

true表示成功,false表示失败

2.4.10、Post

释放信号量(信号量 +1)。

bool Post();

返回值说明:

类型

返回值说明

bool

true表示成功,false表示失败

2.4.10、GetValue

获取信号的值。

int GetValue() const;

返回值说明:

类型

返回值说明

int

返回当前信号量的值

3、程序解析

3.1、创建编译引导


//vendor/lockzhiner/rk3568/samples/BUILD.gn文件添加一行编译引导语句。

import("//build/ohos.gni")

group("samples") {
  deps = [
    "a24_utils_semaphore:utils_semaphore",	# 添加该行
  ]
}

"
a24_utils_semaphore:utils_semaphore",
该行语句表示引入参与编译。

3.2、创建编译项目

创建a24_utils_semaphore目录,并添加如下文件:

a24_utils_semaphore
├── utils_name_semaphore.cpp		    # 有名信号量案例
├── utils_noname_semaphore.cpp          # 无名信号量案例
├── BUILD.gn							# GN文件

3.3、创建BUILD.gn

编辑BUILD.gn文件。

添加2个可执行程序,分别是:

  • utils_noname_semaphore:无名信号量使用案例
  • utils_name_semaphore:有名信号量使用案例
import("//build/ohos.gni")

ohos_executable("utils_noname_semaphore") {
  sources = [ "utils_noname_semaphore.cpp" ]
  include_dirs = [
    "//commonlibrary/c_utils/base/include",
    "//commonlibrary/c_utils/base:utils",
    "//third_party/googletest:gtest_main",
    "//third_party/googletest/googletest/include",
  ]
  external_deps = [ "c_utils:utils" ]
  part_name = "product_rk3568"
  install_enable = true
}

ohos_executable("utils_name_semaphore") {
  sources = [ "utils_name_semaphore.cpp" ]
  include_dirs = [
    "//commonlibrary/c_utils/base/include",
    "//commonlibrary/c_utils/base:utils",
    "//third_party/googletest:gtest_main",
    "//third_party/googletest/googletest/include",
  ]
  external_deps = [ "c_utils:utils" ]
  part_name = "product_rk3568"
  install_enable = true
}

group("utils_semaphore") {
  deps = [
    ":utils_noname_semaphore",
    ":utils_name_semaphore",
  ]
}

注意:

(1)BUILD.gn中所有的TAB键必须转化为空格,否则会报错。如果自己不知道如何规范化,可以:

# 安装gn工具
sudo apt-get install ninja-build
sudo apt install generate-ninja
# 规范化BUILD.gn
gn format BUILD.gn

3.4、无名信号量使用案例

3.4.1、添加信号量头文件

#include <semaphore_ex.h>

3.4.2、创建无名信号量以及公共资源变量

static int m_count = 0;             // 公共资源变量
static OHOS::Semaphore m_sem(1);    // 无名信号量

3.4.3、创建线程池

OHOS::ThreadPool threads("noname_semaphore_threads");

3.4.4、设置线程池

OHOS::ThreadPool threads("noname_semaphore_threads");

3.4.5、启动线程

for (int i = 0; i < threads_start; i++) {
    string str_name = "thread_" + to_string(i);
    auto task = std::bind(func, str_name);
    threads.AddTask(task);
    sleep(1);
}

3.4.6、编写子线程代码

循环5次,每次循环调用信号量Wait()等待获取信号量。如果获取信号量后,将公共资源变量累加,调用信号量Post()释放信号量。

void func(const string &name)
{
    for (int i = 0; i < 5; i++) {
        cout << name << ": Sema Wait..." << endl;
        m_sem.Wait();
        cout << name << ": Sema Wait Successful" << endl;
        m_count += 1;
        m_sem.Post();
        cout << name << ": Sema Post" << endl;
        sleep(1);
    }
}

3.5、有名信号量使用案例

3.5.1、添加信号量头文件

#include <semaphore_ex.h>

3.5.2、创建有名信号量

首先定义有名信号量,然后通过Create()创建全局有名信号量。

int main(int argc, char **argv)
{
    OHOS::NamedSemaphore sem(STRING_NAME_SEMAPHORE, 1);
    ......
    if (!sem.Create()) {
        cout << "NamedSemaphore.Create() failed\n";
        return -1;
    }
    ......
}

3.5.3、创建线程池

通过OHOS::ThreadPool定义线程池,调用SetMaxTaskNum()设置线程池最大线程数,并调用Start()设置当前启动多少个线程。

int main(int argc, char **argv)
{
    OHOS::ThreadPool threads("name_semaphore_threads");
    int threads_start = 3;
    ......
    threads.SetMaxTaskNum(128);
    threads.Start(threads_start);
    ......
}

3.5.4、启动子线程A、B和C

int main(int argc, char **argv)
{
    ......
    // 启动线程A
    str_name = "thread_a";
    auto task_a = std::bind(funcA, str_name);
    threads.AddTask(task_a);
    
    // 启动线程B
    str_name = "thread_b";
    auto task_b = std::bind(funcB, str_name);
    threads.AddTask(task_b);

    // 启动线程A
    str_name = "thread_c";
    auto task_c = std::bind(funcC, str_name);
    threads.AddTask(task_c);
    
    threads.Stop();
    cout << "threads stop" << endl;
    
    return 0;
}

3.5.5、编写子线程A

首先定义有名信号量,信号量数目可以随意设置。

其次,通过Open()打开有名信号量,可以与main()的有名信号量共享同一个信号量。

最后,通过Wait()和Post()来获取释放信号量。

static void funcA(const string &name)
{
    OHOS::NamedSemaphore sem(STRING_NAME_SEMAPHORE, 1);

    cout << get_curtime() << ", " << name << ": start\n";

    // 打开一个已经创建的有名信号量文件
    if (!sem.Open()) {
        cout << get_curtime() << ", " << name << ": sema open failed" << endl;
        return;
    }
    
    for (int i = 0; i < 5; i++) {
        cout << get_curtime() << ", " << name << ": sema wait..." << endl;
        sem.Wait();
        cout << get_curtime() << ", " << name << ": sema wait success" << endl;
        m_count += 1;
        usleep(1000 * 1000 * 1);
        cout << get_curtime() << ", " << name << ": sema count = " << m_count << endl;
        sem.Post();
        cout << get_curtime() << ", " << name << ": sema post and sleep 1 sec" << endl;
        sleep(1);
    }

    cout << get_curtime() << ", " << name << ": end" << endl;
}

3.5.6、编写子线程B

首先定义有名信号量,信号量数目可以随意设置。

其次,通过Open()打开有名信号量,可以与main()的有名信号量共享同一个信号量。

最后,通过TryWait()和Post()来获取释放信号量。

static void funcB(const string &name)
{
    OHOS::NamedSemaphore sem(STRING_NAME_SEMAPHORE, 1);

    cout << get_curtime() << ", " << name << ": start\n";
    
    // 打开一个已经创建的有名信号量文件
    if (!sem.Open()) {
        cout << get_curtime() << ", " << name << ": sema open failed" << endl;
        return;
    }

    for (int i = 0; i < 5; i++) {
        cout << get_curtime() << ", " << name << ": sema trywait..." << endl;
        if (sem.TryWait()) {
            cout << get_curtime() << ", " << name << ": sema trywait success" << endl;
            m_count += 10;
            usleep(1000 * 1000 * 1);
            cout << get_curtime() << ", " << name << ": sema count = " << m_count << endl;
            sem.Post();
            cout << get_curtime() << ", " << name << ": sema post and sleep 1 sec" << endl;
        } else {
            cout << get_curtime() << ", " << name << ": sema tryWait failed and sleep 1 sec" << endl;
        }
        sleep(1);
    }

    cout << get_curtime() << ", " << name << ": end" << endl;
}

3.5.7、编写子线程C

首先定义有名信号量,信号量数目可以随意设置。

其次,通过Open()打开有名信号量,可以与main()的有名信号量共享同一个信号量。

最后,通过TimedWait()和Post()来获取释放信号量。

static void funcC(const string &name)
{
    OHOS::NamedSemaphore sem(STRING_NAME_SEMAPHORE, 1);
    struct timespec ts;

    cout << get_curtime() << ", " << name << ": start\n";

    // 打开一个已经创建的有名信号量文件
    if (!sem.Open()) {
        cout << get_curtime() << ", " << name << ": sema open failed" << endl; 
        return;
    }
    
    for (int i = 0; i < 5; i++) {
        clock_gettime(CLOCK_REALTIME, &ts);
        // 超时等待时间,1秒
        ts.tv_sec += 1;
    
        cout << get_curtime() << ", " << name << ": sema timedwait 1 sec..." << endl;
        if (sem.TimedWait(ts)) {
            cout << get_curtime() << ", " << name << ": sema timedwait success" << endl;
            m_count += 100;
            usleep(1000 * 100);
            cout << get_curtime() << ", " << name << ": sema count = " << m_count << endl;
            sem.Post();
            cout << get_curtime() << ", " << name << ": sema post" << endl;
        } else {
            cout << get_curtime() << ", " << name << ": sema timedwait failed and sleep 1 sec" << endl;
            sleep(1);
        }
    }

    cout << get_curtime() << ", " << name << ": end" << endl;
}

4、编译步骤

进入OpenHarmony编译环境,运行如下命令:

hb build -f

将镜像烧录到开发板中。

5、运行结果

5.1、无名信号量

运行结果如下:

# utils_noname_semaphore
thread_0: Sema Wait...
thread_0: Sema Wait Successful
thread_0: Sema Post
thread_1: Sema Wait...
thread_1: Sema Wait Successful
thread_1: Sema Post
thread_0: Sema Wait...
thread_0: Sema Wait Successful
thread_0: Sema Post
thread_2: Sema Wait...
thread_2: Sema Wait Successful
thread_2: Sema Post
thread_0: Sema Wait...
thread_0: Sema Wait Successful
thread_0: Sema Post
thread_1: Sema Wait...
thread_1: Sema Wait Successful
thread_1: Sema Post
thread_3: Sema Wait...
thread_3: Sema Wait Successful
thread_3: Sema Post
thread_2: Sema Wait...
thread_0: Sema Wait Successful
thread_2: Sema Wait...
thread_2: Sema Post
thread_1: Sema Wait...
thread_0: Sema Wait Successful
thread_0: Sema Post
thread_1: Sema Wait Successful
thread_1: Sema Post
thread_4: Sema Wait...
thread_4: Sema Wait Successful
thread_4: Sema Post
thread_3: Sema Wait...
thread_3: Sema Wait Successful
thread_3: Sema Post
thread_2: Sema Wait...
thread_2: Sema Wait Successful
thread_2: Sema Post
thread_0: Sema Wait...
thread_0: Sema Wait Successful
thread_0: Sema Post
thread_1: Sema Wait...
thread_1: Sema Wait Successful
thread_1: Sema Post
thread_4: Sema Wait...
thread_4: Sema Wait Successful
thread_4: Sema Post
thread_3: Sema Wait...
thread_3: Sema Wait Successful
thread_3: Sema Post
thread_2: Sema Wait...
thread_2: Sema Wait Successful
thread_2: Sema Post
thread_1: Sema Wait...
thread_1: Sema Wait Successful
thread_1: Sema Post
thread_4: Sema Wait...
thread_4: Sema Wait Successful
thread_4: Sema Post
thread_3: Sema Wait...
thread_3: Sema Wait Successful
thread_3: Sema Post
thread_2: Sema Wait...
thread_2: Sema Wait Successful
thread_2: Sema Post
thread_4: Sema Wait...
thread_4: Sema Wait Successful
thread_4: Sema Post
thread_3: Sema Wait...
thread_3: Sema Wait Successful
thread_3: Sema Post
thread_4: Sema Wait...
thread_4: Sema Wait Successful
thread_4: Sema Post
threads stop
# 

5.2、有名信号量

运行结果如下:

# utils_name_semaphore
2017-8-5 19:43:24, thread_a: start
2017-8-5 19:43:24, thread_b: start
2017-8-5 19:43:24, thread_c: start
2017-8-5 19:43:24, thread_a: sema wait...
2017-8-5 19:43:24, thread_a: sema wait success
2017-8-5 19:43:24, thread_b: sema trywait...
2017-8-5 19:43:24, thread_b: sema tryWait failed and sleep 1 sec
2017-8-5 19:43:24, thread_c: sema timedwait 1 sec...
2017-8-5 19:43:25, thread_a: sema count = 1
2017-8-5 19:43:25, thread_c: sema timedwait failed and sleep 1 sec
2017-8-5 19:43:25, thread_a: sema post and sleep 1 sec
2017-8-5 19:43:25, thread_b: sema trywait...
2017-8-5 19:43:25, thread_b: sema trywait success
2017-8-5 19:43:26, thread_a: sema wait...
2017-8-5 19:43:26, thread_c: sema timedwait 1 sec...
2017-8-5 19:43:26, thread_b: sema count = 11
2017-8-5 19:43:26, thread_b: sema post and sleep 1 sec
2017-8-5 19:43:26, thread_a: sema wait success
2017-8-5 19:43:27, thread_c: sema timedwait failed and sleep 1 sec
2017-8-5 19:43:27, thread_b: sema trywait...
2017-8-5 19:43:27, thread_b: sema tryWait failed and sleep 1 sec
2017-8-5 19:43:27, thread_a: sema count = 12
2017-8-5 19:43:27, thread_a: sema post and sleep 1 sec
2017-8-5 19:43:28, thread_c: sema timedwait 1 sec...
2017-8-5 19:43:28, thread_c: sema timedwait success
2017-8-5 19:43:28, thread_b: sema trywait...
2017-8-5 19:43:28, thread_a: sema wait...2017-8-5 19:43:28, thread_b
: sema tryWait failed and sleep 1 sec
2017-8-5 19:43:28, thread_c: sema count = 112
2017-8-5 19:43:28, thread_c: sema post
2017-8-5 19:43:28, thread_c: sema timedwait 1 sec...
2017-8-5 19:43:28, thread_a: sema wait success
2017-8-5 19:43:29, thread_b: sema trywait...
2017-8-5 19:43:29, thread_b: sema tryWait failed and sleep 1 sec
2017-8-5 19:43:29, thread_c: sema timedwait failed and sleep 1 sec
2017-8-5 19:43:29, thread_a: sema count = 113
2017-8-5 19:43:29, thread_a: sema post and sleep 1 sec
2017-8-5 19:43:30, thread_b: sema close
2017-8-5 19:43:30, thread_c: sema timedwait 1 sec...
2017-8-5 19:43:30, thread_c: sema timedwait success
2017-8-5 19:43:30, thread_a: sema wait...
2017-8-5 19:43:31, thread_c: sema count = 213
2017-8-5 19:43:31, thread_c: sema post
2017-8-5 19:43:31, thread_c: sema close
2017-8-5 19:43:31, thread_a: sema wait success
2017-8-5 19:43:32, thread_a: sema count = 214
2017-8-5 19:43:32, thread_a: sema post and sleep 1 sec
2017-8-5 19:43:33, thread_a: sema wait...
2017-8-5 19:43:33, thread_a: sema wait success
2017-8-5 19:43:34, thread_a: sema count = 215
2017-8-5 19:43:34, thread_a: sema post and sleep 1 sec
2017-8-5 19:43:35, thread_a: sema close
threads stop
#

相关文章

Vue3,父组件子组件传值,provide(提供)和inject(注入)传值

父组件向子组件传值父子组件传递数据时,通常使用的是props和emit,父向子传递使用props,子向父传递使用emit。子组件接收3种方式// 1、简单接收 props:["title...

快速上手React(快速上手的高级表达)

web前端三大主流框架1、Angular大家眼里比较牛的框架,甚至有人说三大框架中只有它能称得上一个完整的框架,因为它包含的东西比较完善,包含模板,数据双向绑定,路由,模块化,服务,过滤器,依赖注入等...

100行Html5+CSS3+JS代码实现元旦倒计时界面

一、前言2022年到了,祝大家虎年大吉喜气临,昂首摆尾迎春来。双眼圆睁看世界,万水千山尽开颜。胡须翘翘美姿态,人人开心祝平安。巨大身躯摇摆摆,坎坷困境当笑谈。愿你虎年万事顺,吉星高照旺旺旺!二、202...

育知HTML5培训,为什么要学习“HTML5混合式开发技术”

HTML5 的广泛应用,强势崛起企业现在安卓、iOS开发人员都在学习HTML5混合开发,节约成本、一专多能是未来很多企业用人趋势!HTML5工程师在今后的工作中与 Android、iOS工程师对接的几...

小白友好型Windows优化工具BoosterX使用教程 一键提升游戏性能

一款集系统优化和游戏优化为一体的Windows友好型优化工具,BoosterX得到过众多游戏玩家的推荐,它能简单、快速、安全地优化Windows,就算设置出错还能通过备份还原设置。它还提供了一款精简版...

全屋定制板材怎么选不出错?6 种板材深度对比,3 套方案闭眼选

装修选板材头疼吧?刚装完的柜子变形发霉,说好的环保结果甲醛超标,这种翻车现场见太多了。厨房卫生间千万别用密度板,这玩意儿遇水就膨胀,半年准完蛋。颗粒板性价比确实高,但小厂出的真心不敢用。环保等级至少得...