Structured Concurrency:结构化并发

这篇具有很好参考价值的文章主要介绍了Structured Concurrency:结构化并发。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、参考

https://ericniebler.com/2020/11/08/structured-concurrency/

二、总结

1. 结构化并发是什么-概述

是什么:一种确保子操作在父操作之前完成的方式,类似函数在调用函数之前完成。

最典型的结构化并发:C++20的协程

意义:它通过使异步生存期与普通C++词法作用域相对应,为异步程序带来了现代C++风格,并且不需要引用计数(智能指针,垃圾回收)来管理对象的生命周期

总结:即使在并发环境中,函数嵌套调用时参数的作用域也是严格嵌套的,不需要用智能指针(shared_ptr)之类的技术,也不会发生不小心的内存泄露--对象的生命周期

2. 为什么需要结构化并发

2.1 结构化编程

具有严格的嵌套作用域和生命周期,并发情况下需考虑

2.2 多线程编程的困境

并发场景下编程难度大:

①对象生命周期已经结束,但是其他线程仍需要访问,会导致悬空引用问题,此时需要智能指针通过引用计数来管理其生命周期

②与作用域相关的生命周期不清晰

2.3 非结构化并发

非结构化异步编程很难保证正确性和效率,且编程复杂,且没有办法强制要求子任务在父任务之前完成

3. 结构化并发-深入

3.1 库

协程:Lewis Baker’s popular cppcoro library

提升:

①只需要一个函数

②状态保存在本地变量,而不需要保存在需要的引用计数的共享变量

③可使用一般的c++错误处理技术

从结构保证了子操作在父操作之前完成

3.2 取消机制

从结构保证了子操作在父操作之前完成(也就是说,如果父操作先完成,需要等待子操作)

为了避免为不再需要结果的子操作等待不必要的长时间,我们需要一种能够取消这些子操作的机制,以便它们快速完成。

3.3 结构化并发>协程

结构化并发:特定模式中的回调,可以在没有协程的情况下实现结构化并发

4. 程序示例

场景:存在一个子操作,父任务需要此操作的结果

编译运行环境:ubuntu22.04.3

①单线程:主线程等待子线程结束

耗时操作:computeResult

调用者:doThing

#include <iostream>
#include <unistd.h>

struct State{
	int result;
};

// synchronous
void computeResult(State & s)
{
	int time = 30;
	sleep(time);	// 用sleep模拟耗时操作
	s.result = time;
}

int doThing() {
	State s;
	computeResult(s);
	return s.result;
}

int main()
{
	std::cout << "result: " << doThing() << std::endl;
}

// compile: g++ main.cpp
// output: result:30

关注State s;的声明周期

②使用std::future:获取子线程的结果

#include <future>
#include <iostream>
#include <unistd.h>

struct State{
    int result;
};

int computeResult(State &s)
{
    int time = 30;
    sleep(time);
    s.result = time;
    
    std::cout << "p1" << std::endl;
    
    return 1;
}

std::future<int> doThing() 
{
    std::cout << "p2" << std::endl;

    State s;
    std::future<int> fut = std::async(computeResult, s);
    return fut;
}

int main()
{
    std::cout << "p3" << std::endl;
    auto fut = doThing();
    std::cout << "result:" << fut.get() << std::endl;
}

// compile: g++ main.cpp
// 编译阶段不通过: 无法传递引用类型的函数参数
// main.cpp:24:38: error: no matching function for call to ‘async(int (&)(State&), State&)’
//    24 |     std::future<int> fut = std::async(computeResult, s);

注意State s;的生命周期,使用智能指针shared_ptr管理State s;

#include <future>
#include <iostream>
#include <unistd.h>

struct State{
    int result;
};

int computeResult(std::shared_ptr<State> s)
{
    int time = 30;
    sleep(time);
    (*s).result = time;

    std::cout << "p1" << std::endl;

    return 1;
}

std::future<int> doThing() 
{
    std::cout << "p2" << std::endl;

    std::shared_ptr<State> s = std::make_shared<State>(); 
    std::future<int> fut = std::async(computeResult, s);    // std::async可以传递带有函数及其参数(参数是引用类型无法会出错),但是boost::async传不了多个参数-解决:使用std::bind绑定
    return fut; // std::future没有类似boost::future的then函数
}

int main()
{
    std::cout << "p3" << std::endl;
    auto fut = doThing();
    std::cout << "result:" << fut.get() << std::endl;
}

// compile: g++ main.cpp
// output:
/*p3
p2
result:p1
1*/

②使用boost::future获取结果:父操作需要子操作的结果

参考:http://www.bryh.cn/a/142977.html

注意:使用boost::future需要设置宏#define BOOST_THREAD_PROVIDES_FUTURE

// 写法一
#define BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <iostream>

struct State{
    int result;
};

// asynchronous
boost::future<void> computeResult(State &s)
{
    int time = 30;
    sleep(time);
    s.result = time;
    std::cout << "p1" << std::endl;
}

boost::future<int> doThing() {
    State s;
    auto fut = computeResult(s);
    std::cout << "p2" << std::endl;
    return fut.then(
      [&](auto&&) { return s.result; }); //OOPS
}

int main()
{
    std::cout << "p3" << std::endl;
    auto fut = doThing();
    std::cout << "result:" << fut.get() << std::endl;
}

// compile: g++ main.cpp -lboost_thread
// output: 
/*
p3
p1
p2
terminate called after throwing an instance of 'boost::wrapexcept<boost::lock_error>'
  what():  boost: mutex lock failed in pthread_mutex_lock: Invalid argument
已放弃 (核心已转储)
*/

// 写法二
#define BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <iostream>
#include <memory>
#include <unistd.h>

struct State{
    int result;
};

int computeResult(State &s)
{
    std::cout << "p1" << std::endl;

    int time = 30;
    sleep(time);
    s.result = time;
    return 1;
}

boost::future<int> doThing() {
    std::cout << "p2" << std::endl;

    State s;
    boost::future<int> fut = boost::async(boost::bind(computeResult, s));
    return fut.then(
     [&](auto&&) { return s.result; }); //OOPS
}

int main()
{
    std::cout << "p3" << std::endl;
    auto fut = doThing();
    std::cout << "result:" << fut.get() << std::endl;
}

// compile: g++ main.cpp -lboost_thread
// output: 
/*
p3
p2
result:p1
21978
*/

写法一问题:State s;的生命周期:其超出doThing作用域范围消亡时,computeResult仍可能在访问或使用这个变量,此时引用悬空,引发程序崩溃,即当computeResult线程试图访问或修改已经被销毁的State对象时,会发生未定义的行为。在这种情况下,尝试从返回的future对象中获取结果可能会导致不可预测的结果或程序崩溃。

写法二问题:未获取到正确的值,因为s是局部的,并且当doThing返回时会被销毁,所以尝试访问它的result成员是未定义的行为(标记为"OOPS")

解决:使用智能指针shared_ptr管理

#define BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <iostream>
#include <memory>
#include <unistd.h>

struct State{
    int result;
};

int computeResult(std::shared_ptr<State> s)
{
    std::cout << "p1" << std::endl;

    int time = 30;
    sleep(time);
    (*s).result = time;
    return 1;
}

boost::future<int> doThing() {
    std::cout << "p2" << std::endl;

    std::shared_ptr<State> s = std::make_shared<State>();
    boost::future<int> fut = boost::async(boost::bind(computeResult, s));
    return fut.then(
     [&](auto&&) { return (*s).result; });
}

int main()
{
    std::cout << "p3" << std::endl;
    auto fut = doThing();
    std::cout << "result:" << fut.get() << std::endl;
}

// compile: g++ main.cpp -lboost_thread
// output: 
/*
p3
p2
result:p1
30
*/

④使用结构化并发:cppcoro库---一个用于C++20的协程库,它提供了一个轻量级和易于使用的协程支持

未实际编译文章来源地址https://www.toymoban.com/news/detail-747417.html

cppcoro::task<> computeResult(State & s);
 
cppcoro::task<int> doThing() {
  State s;
  co_await computeResult(s);
  co_return s.result;
}

到了这里,关于Structured Concurrency:结构化并发的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【numpy基础】--结构化

    目前为止,介绍的 numpy 数组基本都是关于数值的,其实, numpy 本身就是一个用于数值计算的基础库。 不过,除了数值计算之外, numpy 也能够支持 结构化数组 。 numpy 的数组为了提高计算性能,要求数组的数据类型要一致。 但是现实情况下,我们经常遇到不是纯数值的数组

    2024年02月12日
    浏览(45)
  • 第五章 结构化设计

    一种软件开发活动,定义实现需求规约所需的软件结构。 结构化设计分为: (1)总体设计:确定系统的整体模块结构,即系统实现所需要的软件模块以及这些模块之间的调用关系。 (2)详细设计:详细描述模块。 体系结构设计(MSD) 接口设计 数据设计 实现软件设计的目标对结

    2024年02月08日
    浏览(60)
  • elasticsearch结构化查询

    在上一篇中我们介绍了DSL相关的知识,接下来我们将会学习elasticsearch的结构化查询,同时也实践一下上一篇的DSL的查询用法 从《Elasticsearch权威指南》上摘取部分解释如下: 从上面的定义我们可以看出来结构化查询最重要的就是是否匹配么人并不是很关心相关性和分值计算。

    2024年02月01日
    浏览(49)
  • elasticsearch结构化查询(一)

    在上一篇中我们介绍了DSL相关的知识,接下来我们将会学习elasticsearch的结构化查询,同时也实践一下上一篇的DSL的查询用法 从《Elasticsearch权威指南》上摘取部分解释如下: 从上面的定义我们可以看出来结构化查询最重要的就是是否匹配么人并不是很关心相关性和分值计算。

    2024年02月05日
    浏览(63)
  • 结构化流的介绍

    目录 有界数据和无界数据 有界数据  无界数据  结构化流 基本介绍 入门案例 结构化流的编程模型 数据结构 数据源(Source) File Source Kafka Source(Spark 和 Kafka 整合) 整合Kafka准备工作 从kafka中读取数据 流式处理 批处理  数据写入Kafka中 流式处理 批处理 有界数据 数据有固定的开

    2024年01月15日
    浏览(63)
  • SQL:结构化查询语言

    创建一张表并插入数据: 以下常用函数以MySQL为例,其它数据库类似

    2024年02月06日
    浏览(50)
  • WPF 界面结构化处理

    WPF 框架是开源的,但是不能跨平台,可以使用MAUI,这个框架可以跨平台,WPF源码可以在github上下载,下载地址:https://gitbub.com/dotnet/wpf。 框架结构 如图 XAML:eXtensible Application Markup Language的英文缩写,相应的中文名称为:可扩展应用程序标记语言。 命名空间 默认 映射:x/

    2024年02月13日
    浏览(62)
  • MATLAB结构化程序设计

    1 、实验目的: 1)巩固并加深对MATLAB语言程序设计知识的理解; 2)掌握和提高MATLAB语言编程和程序调试的基本技能; 3)进一步理解和运用结构化程序设计的思想和方法; 4)提高运用MATLAB语言解决实际问题的能力。 2-1)、分别使用for语句和while语句求1~100的和。 while语句

    2023年04月08日
    浏览(42)
  • 【案例】--非结构化数据中台案例

    最近接触一个平台架构的讨论,公司需要一个非结构化数据中台,理念是能够满足存储随时变换的非结构化数据,另外引入低代码思想。由于非结构化数据是未知的,不同业务的数据是不同,为了更好的使用,低代码就需要一种方案,在尽量不开发代码下满足相关需求变化,

    2024年02月10日
    浏览(60)
  • 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据

    【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 本章节讲解一种自定义数据类型

    2024年02月10日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包