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

目录
  • 一、关于协程
  • 二、协程的好处
  • 三、协程得用法
  • 四、与线程的区别
  • 五、协程示例

一、关于协程

从 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++协程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ 实现即时通信的示例代码(直接运行)

    目录 题目 软件:VS 服务器端 客户端 题目 由于本学期上了网络编程课程,老师要求写使用Socke实现网络编程.于是参考C++多线程实现即时通信软件写出了简单版本的没有界面的即时通信软件. 软件:VS 直接上代码,需要讲解原理的,可以参考C++多线程实现即时通信软件 服务器端 //TcpServer_plus.exe #include<stdio.h> #include <Winsock2.h> #include<WS2tcpip.h> #pragma comment

  • C++广播通信实例

    本文实例讲述了C++实现广播通信的方法.分享给大家供大家参考.具体实现方法如下: 广播通信代码框架: 1. 协议都是: 复制代码 代码如下: SOCKET s = ::socket(AF_INET, SOCK_DGRAM, 0); 2. 服务端设置选项 复制代码 代码如下: BOOL bBroadcast = TRUE;  ::setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL)); 3. 服务端向255

  • C++基于boost asio实现sync tcp server通信流程详解

    目录 一.功能介绍 二.string类型数据交互 2.1 程序源码 2.2 编译&&执行 2.3 程序执行结果 三.byte类型数据交互 3.1 程序源码 3.2 编译&&执行 3.3 程序执行结果 一.功能介绍   基于boost asio实现server端通信,采用one by one的同步处理方式,并且设置连接等待超时.下面给出了string和byte两种数据类型的通信方式,可覆盖基本通信场景需求. 二.string类型数据交互   规定server与client双方

  • C++多线程实现TCP服务器端同时和多个客户端通信

    通讯建立后首先由服务器端发送消息,客户端接收消息:接着客户端发送消息,服务器端接收消息,实现交互发送消息. 服务器同时可以和多个客户端建立连接,进行交互: 在某次交互中,服务器端或某客户端有一方发送"end"即终止服务器与其的通信:服务器还可以继续接收其他客户端的请求,与其他客户端通信. 服务器端 #include <WinSock2.h> #include <WS2tcpip.h> #include <iostream> using namespa

  • C++详细分析线程间的同步通信

    目录 1.多线程编程两个问题 1.1.线程间的互斥 1.2.线程间的同步通信 2.生产者-消费者线程模型 3.lock_gard和unique_lock 4.流程分析 1.多线程编程两个问题 1.1.线程间的互斥 竞态条件: 多线程执行的结果是一致的,不会随着CPU对线程不同的调用顺序,而产生不同的运行结果. 发生竞态条件的代码段,称为临界区代码段(只有一个线程可以进来),保证临界区代码段原子操作,通过线程互斥锁mutex,也可以使用轻量级的无锁实现CAS. C++11的mutex底层实现: 使

  • 浅析C\C++和Lua的通信方式

    为了实现Lua和其他语言之间的通信,Lua虚拟机为C\C++提供了两个特性: 一,Lua_State状态机 lua_State主要是管理一个lua虚拟机的执行环境, 一个lua虚拟机可以有多个执行环境.Lua虚拟机通过维护这样一个虚拟栈来实现两种之间的通信,lua_State定义如下: struct lua_State { CommonHeader; lu_byte status; StkId top; /* first free slot in the stack */ global_Stat

  • C++进程链接工具之通信器详解

    目录 一.传播者 二.示例和代码 一.传播者 本章中的所有示例仅使用一个连接所有进程的通信器.但是,可以创建更多的通信器来链接进程的子集.这对于不需要由所有进程执行的集体操作特别有用. 二.示例和代码 示例 47.15.使用多个通信器 #include <boost/mpi.hpp> #include <boost/serialization/string.hpp> #include <string> #include <iostream> int main

  • C++ Thread实现简单的socket多线程通信

    目录 起因 服务端 ROS客户端 普通客户端 运行效果 不足 起因 为什么要用C++的Thread,很简单,因为我菜 一打五用pthread实现了socket多线程通信,我之前学并发的时候没看pthread,因此代码只能看个大概,后面还是要系统学一下pthread的 服务端 多线程功能放在腾讯云服务器上,代码如下: #include "tcpserver.h" #include <thread> #include <mutex> TcpServer server

  • C++线程间的互斥和通信场景分析

    互斥锁(mutex) 为了更好地理解,互斥锁,我们可以首先来看这么一个应用场景:模拟车站卖票. 模拟车站卖票 场景说明: Yang车站售卖从亚特兰蒂斯到古巴比伦的时光飞船票:因为机会难得,所以票数有限,一经发售,谢绝补票. 飞船票总数:100张: 售卖窗口:3个. 对于珍贵的飞船票来说,这个资源是互斥的,比如第100张票,只能卖给一个人,不可能同时卖给两个人.3个窗口都有权限去售卖飞船票(唯一合法途径). 不加锁的结果 根据场景说明,我们可以很快地分析如下: 可以使用三个线程来模拟三个独立的窗口

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

    目录 一.关于协程 二.协程的好处 三.协程得用法 四.与线程的区别 五.协程示例 一.关于协程 从 1.54.0 版本开始,Boost.Asio 支持协程.虽然您可以直接使用 Boost.Coroutine,但 Boost.Asio 中对协程的显式支持使得使用它们变得更加容易. 协程让您创建一个反映实际程序逻辑的结构.异步操作不会拆分函数,因为没有处理程序来定义异步操作完成时应该发生什么.程序可以使用顺序结构,而不是让处理程序相互调用. 二.协程的好处 考虑多任务协作的场景. 如果是线程的并发

  • C++20 新特性 协程 Coroutines(2)

    目录 1.co_await 2.awaiter 的三个接口用途 3.协程用法的回顾 想了解上一篇文章内容的小伙伴可点击 C++20 特性 协程 Coroutines (1) 谈到什么是协程. 并且介绍了 co_yield 和 co_return 的作用. 这篇来介绍一下 co_await. 1.co_await 一个形如: co_await awaitable 的表达式就叫一个 await-expression. co_await 表达式是用来暂停当前协程的运行, 转而等待 awaitable

  • Java 8新特性方法引用详细介绍

    Java 8新特性方法引用 对于引用来说我们一般都是用在对象,而对象引用的特点是:不同的引用对象可以操作同一块内容! Java 8的方法引用定义了四种格式: 引用静态方法     ClassName :: staticMethodName 引用对象方法:  Object:: methodName 引用特定类型方法: ClassName :: methodName 引用构造方法: ClassName  :: new  静态方法引用示例 /** * 静态方法引用 * @param <P> 引用方法

  • C++20 特性 协程 Coroutines(1)

    目录 一.协程简单介绍 二.协程的好处 三.协程得用法 四.协程三个关键字 五.协程工作原理 1.co_yield 2.co_return 我们先来介绍一下什么是协程. 一.协程简单介绍 协程和普通的函数 其实差不多. 不过这个 "函数" 能够暂停自己, 也能够被别人恢复. 普通的函数调用, 函数运行完返回一个值, 结束. 协程可以运行到一半, 返回一个值, 并且保留上下文. 下次恢复的时候还可以接着运行, 上下文 (比如局部变量) 都还在. 这就是最大的区别. 二.协程的好处 考虑多

  • Vue生态的新成员Pinia的详细介绍

    目录 安装和配置 Store核心 State Getters Actions Vue Devtools 最后 结论 参考文献 Pinia是Vue应用程序的状态管理方案,是Vuex核心团队成员开发.感觉更像是常规的旧 javascript 导入模块,实现了很多Vuex5的提案. Pinia同时支持Vue2和Vue3,不过下面展示的例子都是使用Vue3,Pinia的版本是Pinia@2.0.9. Vue2和Vue3使用的Pinia版本有一点不同,因此请查看官方Pinia 文档以获取更多信息. 安装和

  • Java8新特性之字符串去重介绍

    8月19日,Oracle发布了JDK 8u20,JDK 8u20包含很多新特性,比如Java编译器更新.支持在运行时通过API来修改MinHeapFreeRatio和MaxHeapFreeRatio参数.新的GC调优指南文档.不过在众多新特性中,最令人期待的还属字符串去重(String Deduplication ).如何减少内存占用一直是一个永恒的话题,而在Java应用中,经常会看到String对象会占用应用30%的内存,它是Java中最常用的对象之一.新的字符串去重特性可以帮助减少应用中St

  • PHP7新特性foreach 修改示例介绍

    一.foreach()循环对数组内部指针不再起作用,在PHP7之前,当数组通过foreach迭代时,数组指针会移动.现在开始,不再如此,见下面代码.. $array = [0, 1, 2]; foreach ($array as &$val) { var_dump(current($array)); } PHP5运行的结果会打印int(1) int(2) bool(false) PHP7运行的结果会打印三次int(0),也就是说数组的内部指针并没有改变. 之前运行的结果会打印int(1), in

  • Docker 特性与原理详细介绍与解析

    Docker 特性与原理 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上,例如运行一个一次性交互shell 文件系统隔离:每个进程容器运行在完全独立的根文件系统里 写时复制:采用写时复制方式创建根文件系统,这让部署变得极其快捷,并且节省内存和硬盘空间 资源隔离:可以使用cgroup为每个进程容器分配不同的系统资源 网络隔离:每个进程容器运行在自己的网络命名空间里,拥有自己的虚拟接口和IP地址 日志记录:Docker将会收集和记

  • Kotlin协程flowOn与线程切换超详细示例介绍

    目录 示例代码 一.flowOn方法 1.ChannelFlowOperatorImpl类 二.collect方法 1.ChannelFlowOperator类的collect方法 2.ChannelFlow类的collect方法 3.flow方法中代码的执行 4.接收flow方法发出的值 三.flowOn方法与流的融合 四.总结 示例代码 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.flowOn(Dispatc

  • Kotlin协程开发之Flow的融合与Channel容量及溢出策略介绍

    目录 一.协程间的通信 1.通道容量 2.溢出策略 二.FusibleFlow接口 三.ChannelFlow类 一.协程间的通信 当需要进行协程间的通信时,可以调用Channel方法,创建一个Channel接口指向的对象,通过调用该对象的send方法和receive方法实现消息的发送与接收.协程对Channel接口的实现,本质上与阻塞队列类似,这里不再赘述. 1.通道容量 事实上,send方法与receive方法并没有定义在Channel接口中,而是分别定义在SendChannel接口和Rec

随机推荐