C++通信新特性协程详细介绍

 更新时间:2022年10月31日 10:32:23   作者:无水先生  
这篇文章主要给大家分享得是C++的特性协程Coroutine,下面文章内容我们将来具体介绍什么是协程,协程得好处等知识点,需要的朋友可以参考一下

一、关于协程

从 1.54.0 版本开始,Boost.Asio 支持协程。虽然您可以直接使用 Boost.Coroutine,但 Boost.Asio 中对协程的显式支持使得使用它们变得更加容易。

协程让您创建一个反映实际程序逻辑的结构。异步操作不会拆分函数,因为没有处理程序来定义异步操作完成时应该发生什么。程序可以使用顺序结构,而不是让处理程序相互调用。

二、协程的好处

考虑多任务协作的场景. 如果是线程的并发, 那么大家需要抢 CPU 用, 还需要条件变量/信号量或者上锁等技术, 来确保正确的线程正在工作.

如果在协程中, 大家就可以主动暂停自己, 多个任务互相协作. 这样可能就比大家一起抢 CPU 更高效一点, 因为你能够控制哪个协程用上 CPU.

一个例子:

生产者/消费者模型: 生产者生产完毕后, 暂停自己, 把控制流还给消费者. 消费者消费完毕后, resume 生产者, 生产者继续生产. 这样循环往复.

异步调用: 比如你要请求网络上的一个资源.

  • 发请求给协程
  • 协程收到请求以后, 发出请求. 协程暂停自己, 把控制权还回去.
  • 你继续做些别的事情. 比如发出下一个请求. 或者做一些计算.
  • 恢复这个协程, 拿到资源 (可能还要再等一等)

理想状态下, 4 可以直接用上资源, 这样就完全不浪费时间.

如果是同步的话:

  • 发请求给函数.
  • 函数收到请求以后, 等资源.
  • 等了很久, 资源到了, 把控制权还回去.

明显需要多等待一会儿. 如果需要发送上百个请求, 那显然是第一种异步调用快一点. (等待的过程中可以发送新的请求)

如果没有协程的话, 解决方案之一是使用多线程. 像这样:

  • 发请求给函数.
  • 函数在另外的线程等, 不阻塞你的线程.
  • 你继续做些别的事情. 比如发出下一个请求. 或者做一些计算.
  • 等到终于等到了, 他再想一些办法通知你.

然后通知的办法就有 promise 和回调这些办法.

三、协程得用法

我们照着 C++20 标准来看看怎么用协程. 用 g++, 版本 10.2 进行测试.

目前 C++20 标准只加入了协程的基本功能, 还没有直接能上手用的类. GCC 说会尽量与 clang MSVC 保持协程的 ABI 兼容, 同时和 libc++ 等保持库的兼容. 所以本文可能也适用于它们.

协程和主程序之间通过 promise 进行通信. promise 可以理解成一个管道, 协程和其调用方都能看得到.

以前的 std::async std::future 也是基于一种特殊的 promise 进行通信的, 就是 std::promise. 如果要使用协程, 则需要自己实现一个全新的 promise 类, 原理上是类似的.

四、与线程的区别

线程处于进程之中,协程处于线程之中,线程有系统内核调度,而协程有程序员自己调度。一个线程可以有多个协程,而且只要内存足够,一个线程中可以有任意多个协程;但某一时刻只能有一个协程在运行,多个协程分享该线程分配到的计算机资源。协程是追求极限性能和优美的代码结构的产物。

使用过程中需要包含#include <boost/coroutine2/all.hpp>,链接动态库:-lboost_coroutine -lboost_context。关于使用boost库错

协程有如下特点:

  1. 同其他数据类型一样,协程也是第一类(first-class)对象,可以被当参数传递等操作;
  2. 运行特点是挂起运行,离开协程,过后再进入,恢复运行;
  3. 具有对称和非对称的转移控制机制;
  4. 挂起前和恢复后本地变量的值是一致的;
  5. 有stackless和stackful两种类型

五、协程示例

示例 32.7。使用 Boost.Asio 的协程

#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <list>
#include <string>
#include <ctime>
using namespace boost::asio;
using namespace boost::asio::ip;
io_service ioservice;
tcp::endpoint tcp_endpoint{tcp::v4(), 2014};
tcp::acceptor tcp_acceptor{ioservice, tcp_endpoint};
std::list<tcp::socket> tcp_sockets;
void do_write(tcp::socket &tcp_socket, yield_context yield)
{
  std::time_t now = std::time(nullptr);
  std::string data = std::ctime(&now);
  async_write(tcp_socket, buffer(data), yield);
  tcp_socket.shutdown(tcp::socket::shutdown_send);
}
void do_accept(yield_context yield)
{
  for (int i = 0; i < 2; ++i)
  {
    tcp_sockets.emplace_back(ioservice);
    tcp_acceptor.async_accept(tcp_sockets.back(), yield);
    spawn(ioservice, [](yield_context yield)
      { do_write(tcp_sockets.back(), yield); });
  }
}
int main()
{
  tcp_acceptor.listen();
  spawn(ioservice, do_accept);
  ioservice.run();
}

调用 Boost.Asio 使用协程的函数是 boost::asio::spawn()。传递的第一个参数必须是 I/O 服务对象。第二个参数是将成为协程的函数。此函数必须接受 boost::asio::yield_context 类型的对象作为其唯一参数。它必须没有返回值。示例 32.7 使用 do_accept() 和 do_write() 作为协程。如果函数签名不同,例如 do_write() 的情况,您必须使用类似 std::bind 的适配器或 lambda 函数。

您可以将 boost::asio::yield_context 类型的对象而不是处理程序传递给异步函数。 do_accept() 将参数 yield 传递给 async_accept()。在 do_write() 中,yield 被传递给 async_write()。这些函数调用仍会启动异步操作,但在操作完成时不会调用任何处理程序。而是恢复启动异步操作的上下文。当这些异步操作完成时,程序会从中断的地方继续。

do_accept() 包含一个 for 循环。每次调用该函数时,都会将一个新套接字传递给 async_accept()。一旦客户端建立连接,do_write() 将作为协程调用,并带有 boost::asio::spawn() 以将当前时间发送给客户端。

for 循环可以很容易地看出程序在退出之前可以为两个客户端提供服务。由于该示例基于协程,因此可以在 for 循环中实现异步操作的重复执行。这提高了程序的可读性,因为您不必跟踪对处理程序的潜在调用来找出最后一个异步操作何时完成。如果时间服务器需要支持两个以上的客户端,则只需调整 for 循环。

到此这篇关于C++通信新特性协程详细介绍的文章就介绍到这了,更多相关C++协程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言的冒泡排序和快速排序算法使用实例

    C语言的冒泡排序和快速排序算法使用实例

    这篇文章主要介绍了C语言的冒泡排序和快速排序算法使用实例,示例题目也是ACM练习当中的基础习题,需要的朋友可以参考下
    2015-08-08
  • 深入理解c++指针的指针和指针的引用

    深入理解c++指针的指针和指针的引用

    下面小编就为大家带来一篇深入理解c++指针的指针和指针的引用。小编觉得挺不错的,现在就分享给大家,也给大家做个参考,一起跟随小编过来看看吧
    2016-06-06
  • 使用emacs编写C语言教程

    使用emacs编写C语言教程

    这篇文章主要介绍了使用emacs编写C语言教程,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C语言中数组常用的一些排序算法小结

    C语言中数组常用的一些排序算法小结

    数组的排序方法有很多,效率也各不相同,下面这篇文章主要给大家介绍了关于C语言中数组常用的一些排序算法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • C语言实例讲解四大循环语句的使用

    C语言实例讲解四大循环语句的使用

    C语言有四大循环语句,他们之间可以进行任意转换。本文将首先对其语法进行讲解,然后通过一个实例用四种循环来实现。相信通过本文的学习,大家都能够对C语言循环语句有着熟练的掌握
    2022-05-05
  • C++内存模型和名称空间详解

    C++内存模型和名称空间详解

    这篇文章主要给大家介绍了关于C/C++中的内存模型和名称空间详解,文中通过示例代码介绍的非常详细,对大家学习或者使用c/c++具有一定的参考学习价值,需要的朋友们下面随着小编来一起看看吧
    2021-09-09
  • 深入了解C语言栈的创建

    深入了解C语言栈的创建

    栈只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表,但是限定这种线性表只能在某一端进行插入和删除操作,这篇文章主要介绍了C语言对栈的实现基本操作
    2021-07-07
  • 详解C语言sscanf()函数、vsscanf()函数、vscanf()函数

    详解C语言sscanf()函数、vsscanf()函数、vscanf()函数

    这篇文章主要介绍了详解C语言sscanf()函数、vsscanf()函数、vscanf()函数,是C语言入门学习中的基础知识,需要的朋友可以参考下
    2015-08-08
  • C语言指针超详细讲解下篇

    C语言指针超详细讲解下篇

    指针提供了对地址操作的一种方法,因此,使用指针可使得 C 语言能够更高效地实现对计算机底层硬件的操作。另外,通过指针可以更便捷地操作数组。在一定意义上可以说,指针是 C 语言的精髓
    2022-04-04
  • 简要对比C语言中三个用于退出进程的函数

    简要对比C语言中三个用于退出进程的函数

    这篇文章主要介绍了C语言中三个用于退出进程的函数的对比,分别为_exit()函数和on_exit()函数以及atexit()函数,需要的朋友可以参考下
    2015-08-08

最新评论