首页 > 编程语言 >c++协程注意

c++协程注意

时间:2022-10-02 22:06:37浏览次数:97  
标签:异步 协程 协待 c++ 注意 io 超时 连接


类 客户{
公:
客户(){
线=线程([本]{
io环境_.跑();
});
}

简单异步::协程::懒<极>异步连接(动 主机,动 端口){
极 中=协待 工具::异步连接(主机,端口);//.1
协中 中;//.2
}

~客户(){
io环境_.停止();
如(线.可合并()){
线.合并();
}
}

私:
异网::io环境 io环境_;
线程 线;
};

整 主(){
客户 c;
简单异步::协程::同步等待(c.异步连接());
输出<<"退出\n";//.3
}

该示例很简单,​​客户​​​在连接后就析构了,没什么问题.但是运行之后就会有​​合并​​​线程的错误,错误即在线程里​​合并​​​自己了.为什么?​​协待​​​异步连接的协程,当​​连接成功​​​后协程返回,此时切换了​​线程​​​.
​​​异步连接​​​返回时是在​​io环境​​​的线程里,代码中的​​.1​​​在主线程中,​​.2​​​在​​io环境​​​线程,之后就​​协中​​​返回到​​主​​​函数的​​.3​​​,此时​​.3​​​仍然在​​io环境​​​线程里,接着​​客户​​​就会析构了,此时仍然在​​io环境​​​线程里,析构时会调用​​线.合并();​​​,然后就有了在​​io环境​​​的线程里​​合并​​​自己的错误.
这是使用​​​协程​​​时容易犯错的地方,解决方法就是避免​​协待​​​回来之后去析构​​客户​​​,或​​协待​​​仍然返回到​​主线程​​​.这里可考虑用​​协程条件变量​​​,在​​异步连接​​​时发起​​新的协程​​​并传入​​协程条件变量​​​,并在​​连接返回​​​后​​置值​​​,主线程​​协待​​​该条件变量,这样​​连接返回​​​后就回到主线程了,就可解决在​​io​​​线程里​​合并​​自己的问题了.


增加超时处理.

类 客户{
公:
客户():套接字_(io环境_){
线=线程([本]{
io环境_.跑();
});
}

简单异步::协程::懒<极>异步连接(动 主机,动 端口,动 时长){
协程计时器 计时器(io环境_);
超时(计时器,时长).开始([](动&&){});
//.1,启动新协程,来超时处理
极 中=协待 工具::异步连接(主机,端口,套接字_);//假设这里`协待`返回后回到`主线程`
协中 中;
}

~客户(){
io环境_.停止();
如(线.可合并()){
线.合并();
}
}


私:
简单异步::协程::懒<空>超时(动&计时器,动 时长){
极 是超时=协待 计时器.异步等待(时长);
如(是超时){
异网::错误码 忽略误码;
套接字_.关闭(传控::套接字::都关闭,忽略误码);
套接字_.关闭(忽略误码);
}

协中;
}

异网::io环境 io环境_;
传控::套接字 套接字_;
线程 线;
极 是超时_;
};

整 主(){
客户 c;
简单异步::协程::同步等待(c.异步连接("本地主机","9000",5s));
输出<<"退出\n";#3
}

该代码增加了​​连接超时处理​​​的协程,注意​​.1​​​那里为何新启动​​协程​​​,而不能用​​协待​​​呢?因为​​协待​​​是阻塞语义,​​协待​​​会永远超时,启动​​新协程​​​不会​​阻塞​​​当前协程,从而可去调用​​异步连接​​​.
当​​​超时​​​超时时就关闭​​套接字​​​,此时​​异步连接​​​就会​​返回错误​​​然后​​返回​​​到调用者,这似乎可超时处理​​异步连接​​​了,但是​​该代码​​​有问题.假如​​异步连接​​​没有超时会怎样?没有超时的话就返回到​​主​​​函数了,然后​​客户​​​就析构了,当​​超时​​​协程​​恢复​​​回来时​​客户​​​其实已析构了,此时再去调用成员变量​​套接字关闭​​​访问,会有​​已析构对象​​​错误.
也许有人会说,那就再​​​协中​​​之前去取消​​计时器​​​不就好了吗?该办法也不行,因为取消​​计时器​​​,​​超时​​​协程并不会立即返回,仍然会存在​​访问​​已析构对象的问题.

正确做法应该是​​同步​​​两个协程,​​超时​​​协程和​​异步连接​​​协程需要同步,在​​异步连接​​​协程返回之前需要确保已完成​​超时​​​协程,这样就可​​避免​​​访问​​已析构对象​​​.
该问题其实也是​​​异步回调安全返回​​​的经典问题,​​协程​​​也同样会遇见该问题,上面提到的​​同步​​​两个协程是解决方法之一,另外一个方法就是就像​​异步安全回调​​​那样,使用​​shared_from_this​​.


简单异步::协程::懒<极>异步连接(常 串&主机,常 串&端口){
协中 协待 工具::异步连接(主机,端口);
}

简单异步::协程::懒<空>测试连接(){
极 好=协待 异步连接("本地主机","8000");
如(!好){
输出<<"失败,";
}

输出<<"成功";
}

整 主(){
简单异步::协程::同步等待(测试连接());
}
//改后.
简单异步::协程::懒<空>测试连接(){
动 懒=异步连接("本地主机","8000");
极 好=协待 懒;
如(!好){
输出<<"失败";
}

输出<<"成功";
}

代码简单明了,就是测试​​异步连接​​​是否成功,运行也是正常的.如果如上稍微​​改一下​​​ 很遗憾,​​该代码​​会使连接总是失败,似乎很奇怪,后面发现​​原因​​是,因为​​异步连接​​的两个参数​​失效​​了,但是​​写法​​和刚开始的​​写法​​几乎一样,为啥​​后面​​该写法会使​​参数​​失效呢?
原因是​​协待​​协程函数时,其实做了两件事:
1,调用​​协程函数​​创建协程,该步骤会创建​​协程帧​​,把​​参数和局部变量​​拷贝到​​协程帧​​里;
2,​​协待​​执行​​协程函数​​;
再看​​auto lazy=异步连接("localhost","8000");​​,该代码调用​​协程函数​​创建了​​协程​​,此时​​拷贝​​到​​协程帧​​里面的是​​两个临时变量​​,该行​​结束​​时临时变量就析构了,下一行​​协待​​执行​​该协程​​时就会出现​​参数失效​​问题.

​协待 异步连接("localhost","8000");​​​这样为什么没问题呢,因为​​协程​​​创建和​​协程调用​​​都在​​一行内​​​完成的,​​临时变量​​​知道​​协程执行​​​之后才会失效,因此不会有问题.
问题本质其实是​​​C++​​​临时变量​​生命期​​​问题.使用​​协程​​​时稍微注意一下就好了,可把​​const std::string&​​​改成​​std::string​​​,这样就不会有​​临时变量​​​生命期问题了,如果不想改参数类型就​​协待​​​协程函数就好了,不要分成​​两行​​去执行协程.


标签:异步,协程,协待,c++,注意,io,超时,连接
From: https://blog.51cto.com/u_4139404/5729474

相关文章

  • C++工程中的(.h文件)和(.cpp)文件如何管理
    整个工程的格式应该是这样的:#include"initiate_maze.hpp"voidinitiate_maze(intn,intm){srand(time(NULL));vector<string>things;stringa......
  • C++——指针
    一、指针的定义1int*p;2int*p;3int*p;4int*p;首先可以肯定的是上述四种写法(未初始化)都是可以的(本人推荐第一种,不因为什么,看着舒服!!!......
  • 为python编译C++模块时一定要注意的事情—————不要在anaconda环境下使用cmake来编
    平时搞python的人很多都会有安装C++扩展模块的需求,而往往这些C++模块都是使用CMAKE做编译配置的,但是如果你这时候shell环境是使用anaconda的话,那么cmake默认调用的GCC和G++......
  • C++——参数传递
    前言:形式参数(形参):出现在函数定义的地方,形参之间以“,”分隔,其规定了函数接受数据的类型和数量(参数的数目)实际参数(实参):出现在函数调用的地方,用于初始化形参(实参的......
  • c/c++ 时间函数
    c语言相关函数#include<stdio.h>#include<time.h>voidtime_test_func(){time_tseconds;seconds=time(NULL);printf("从1970-01-0100:00:00到......
  • 《Effective C++:改善程序与设计的55个具体做法》阅读笔记
    Item13:使用对象管理资源资源管理对象:资源管理对象管理着其他对象的资源,当资源管理对象的析构函数被调用时,所管理的资源会被自动释放。资源管理对象就是在其析构函数中......
  • C++——史蒂夫
    #include<iostream>#include<string>#include<windows.h>#include"minecraft.h"usingnamespacestd;TxMinecraftmc;intmain(intargc,char**argv){//连接我的世......
  • gym.ObservationWrapper使用时的注意点——reset和step函数可以覆盖observation函数
    记录一个刚学习到的gym使用的点,就是gym.ObservationWrapper使用时的注意点——reset和step函数可以覆盖observation函数。  给出代码:importgymclassWrapper(gym.Observa......
  • C/C++ 实现INI配置文件读写 [转载]
    INI文件是一种标准的Windows平台配置文件,通常这种配置文件用于保存系统软件的一些基本配置参数,如下代码是本人从网络上收集到的一段纯C++编写的配置解析......
  • C++实现双向RRT算法
    C++实现双向RRT算法背景介绍RRT(Rapidly-exploringRandomTrees)是StevenM.LaValle和JamesJ.KuffnerJr.提出的一种通过所及构建空间搜索树实现对非凸高维空间快速搜......