热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

一、asio使用教程–基本技能

本教程使用了简单的异步计时器演示了asio的基本使用。同步使用定时器如何实现阻塞等待定时器。首先引入头文件#include#include

本教程使用了简单的异步计时器演示了asio的基本使用。


同步使用定时器

如何实现阻塞等待定时器。首先引入头文件

#include
#include

"asio.hpp"可以简单地帮我们将所需的头文件引入。

使用asio的所有程序都需要至少一个I/O execution context,像io_context或者thread_pool对象。通过I/O execution context我们来访问I/O功能。

在主函数中声明一个io_context的对象

int main() {
boost::asio::io_context io;
}

接下来再声明一个boost::asio::steady_timer类型的对象。asio中提供I/O功能的核心类(例如本例中的定时器)总是将io_context作为第一个参数。在本例的定时器中,将过期时间设置为第二个参数。

boost::asio::steady_timer t(io,boost::asio::chrono::seconds(5));

在这个简单的例子中,我们对定时器执行阻塞等待。也就是说,对 steady_timer::wait() 的调用将不会返回,直到定时器到期,即创建后 5 秒。

定时器始终处于以下两种状态之一:“过期”或“未过期”。 如果对过期的定时器调用了 steady_timer::wait() 函数,它将立即返回。

t.wait();

最后,在定时器到期后,我们习惯性地打印“Hello,World!” 。

完整代码如下:

#include
#include
int main()
{
boost::asio::io_context io;
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5));
t.wait();
std::cout <<"Hello, world!" < return 0;
}

异步使用定时器

实现一个异步等待的定时器。

使用 asio 的异步功能意味着拥有一个回调函数,该函数将在异步操作完成时被调用。 在这个程序中,我们定义了一个名为 print 的函数,在异步等待完成时调用它。

void print(const boost::system::error_code& /*e*/)
{
std::cout <<"Hello, world!" <}
int main()
{
boost::asio::io_context io;
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5));

接下来与同步方式不同的是,我们让定时器异步等待,并将上面定义的回调函数传递过去

t.async_wait(&print);

最后,我们必须要调用io_context::run()成员函数。

因为asio保证异步回调函数只会在调用io_context::run()函数的线程中执行。所以,不调用该函数,就永远无法取得异步函数执行结果并进行回调。

只要还有异步操作没有完成,那么io_context::run()函数就不会返回。

完整代码如下:

#include
#include
void print(const boost::system::error_code& /*e*/)
{
std::cout <<"Hello, world!" <}
int main()
{
boost::asio::io_context io;
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5));
t.async_wait(&print);
io.run();
return 0;
}

为Handler绑定参数

本节演示如何为Handler附加参数。

我们要实现每秒定时打印一次信息,最多打印5次。

引入头文件

#include
#include
#include

要实现每秒打印,那么我们就要在回调函数中更改定时器的到期时间,然后开始新的异步等待。这就意味着,在回调函数中我们要能够访问定时器对象和当前的次数,所以需要添加两个参数:



  • 指向定时器对象的指针

  • 当前计数值

void print(const boost::system::error_code& /*e*/,
boost::asio::steady_timer* t, int* count)
{
if (*count <5)
{
std::cout <<*count < ++(*count);

接下来,我们将定时器的到期时间从前一个到期时间向前移动一秒。 通过旧的时间计算新的到期时间,我们可以确保计时器不会由于处理处理程序的任何延迟而偏离整秒标记。

t->expires_at(t->expiry() + boost::asio::chrono::seconds(1));

steady_timer::async_wait() 函数需要一个签名为 void(const boost::system::error_code&) 的处理函数(或函数对象),我们使用boost::bind()函数来讲print函数转为与要求函数签名一致的函数对象。

在此示例中,boost::bind()boost::asio::placeholders::error 参数是传递给处理程序的错误对象的命名占位符。 启动异步操作时,如果使用 boost::bind(),则必须仅指定与处理程序的参数列表匹配的参数。

t->async_wait(boost::bind(print,
boost::asio::placeholders::error, t, count));
}
}

完整代码如下:

#include
#include
#include
void print(const boost::system::error_code& /*e*/,
boost::asio::steady_timer* t, int* count)
{
if (*count <5)
{
std::cout <<*count < ++(*count);
//修改过期时间
t->expires_at(t->expiry() + boost::asio::chrono::seconds(1));
//启动异步等待
t->async_wait(boost::bind(print,
boost::asio::placeholders::error, t, count));
}
}
int main()
{
boost::asio::io_context io;
int count = 0;
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1));
t.async_wait(boost::bind(print,
boost::asio::placeholders::error, &t, &count));
io.run();
std::cout <<"Final count is " < return 0;
}

使用成员函数作为Handler

本节中,我们使用类的一个成员函数作为回调函数,实现与上一节相同的功能。

不同于上一节使用print函数作为回调函数,这里我们使用print类作为回调处理器。

我们将定时器与计数都封装在内部,在构造函数中就启动异步操作

class Printer
{
public:
Printer(boost::asio::io_context* ioc) : timer_(ioc)
{
timer_.async_wait(boost::bind(&Printer::print,this));
}
~Printer()
{
std::cout <<"Final count is " < }
void print()
{
if(count_ <5)
{
std::cout < ++count_;
timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1));
timer_.async_wait(boost::bind(&Printer::print,this));
}
}
private:
boost::asio::steady_timer timer_;
int count_;
};

完整代码如下:

#include
#include
#include
class printer
{
public:
printer(boost::asio::io_context& io)
: timer_(io, boost::asio::chrono::seconds(1)),
count_(0)
{
timer_.async_wait(boost::bind(&printer::print, this));
}
~printer()
{
std::cout <<"Final count is " < }
void print()
{
if (count_ <5)
{
std::cout < ++count_;
timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1));
timer_.async_wait(boost::bind(&printer::print, this));
}
}
private:
boost::asio::steady_timer timer_;
int count_;
};
int main()
{
boost::asio::io_context io;
printer p(io);
io.run();
return 0;
}

多线程程序中的同步处理程序

我们知道调用了io_context::run()的函数会执行回调函数。前面几节中,我们都只在一个线程中调用了该函数,因此不会并发执行。

在使用 asio 开发应用程序时,单线程方法通常是最好的方法。 缺点是它对程序(尤其是服务器)的限制,包括:



  • 当处理程序可能需要很长时间才能完成时,响应能力差。

  • 无法在多处理器系统上扩展。

如果您发现自己遇到了这些限制,另一种方法是让线程池调用 io_context::run()。然而,由于这允许处理程序并发执行,当处理程序可能访问共享的、线程不安全的资源时,我们需要一种同步方法。

在上一节的基础上,我们使用两个定时器,总共输出10条信息后结束。

我们同样定义一个Printer类,将其扩展为并行运行两个定时器。

除了初始化一对 boost::asio::steady_timer 成员之外,构造函数还初始化了 strand_ 成员,它是 boost::asio::strand 类型的对象。

strand类模板是一个执行器适配器,它保证通过它分派的处理程序能够在启动下一个处理程序之前完成一个正在执行的处理程序。无论调用io context::run()的线程数量如何,这都是可以保证的。当然,处理程序仍然可以与其他处理程序并发执行,这些处理程序不是通过一个链分派的,或者是通过一个不同的链对象分派的。

class printer
{
printer(boost::asio::io_context& io)
: strand_(boost::asio::make_strand(io)),
timer1_(io, boost::asio::chrono::seconds(1)),
timer2_(io, boost::asio::chrono::seconds(1)),
count_(0)
{
};

启动异步操作时,每个回调处理程序都“绑定”到一个 boost::asio::strand 对象。 boost::asio::bind_executor() 函数返回一个新的处理程序,该处理程序通过strand对象自动分派其包含的处理程序。 通过将处理程序绑定到同一条strand,我们确保它们不能同时执行

timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print1, this)));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
~printer()
{
std::cout <<"Final count is " < }

在多线程程序中,如果异步操作的处理程序访问共享资源,则它们应该同步。 在本教程中,处理程序(print1 和 print2)使用的共享资源是 std::coutcount_数据成员。

void print1()
{
if (count_ <10)
{
std::cout <<"Timer 1: " < ++count_;
timer1_.expires_at(timer1_.expiry() + boost::asio::chrono::seconds(1));
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print1, this)));
}
}
void print2()
{
if (count_ <10)
{
std::cout <<"Timer 2: " < ++count_;
timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
}
private:
boost::asio::strand strand_;
boost::asio::steady_timer timer1_;
boost::asio::steady_timer timer2_;
int count_;
};

现在,主函数会导致从两个线程调用 io_context::run():主线程和一个附加线程。 这是使用boost::thread对象完成的。

int main()
{
boost::asio::io_context io;
printer p(io);
boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
io.run();
t.join();
return 0;
}

完整代码如下所示:

#include
#include
#include
#include
class printer
{
public:
printer(boost::asio::io_context& io)
: strand_(boost::asio::make_strand(io)),
timer1_(io, boost::asio::chrono::seconds(1)),
timer2_(io, boost::asio::chrono::seconds(1)),
count_(0)
{
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print1, this)));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
~printer()
{
std::cout <<"Final count is " < }
void print1()
{
if (count_ <10)
{
std::cout <<"Timer 1: " < ++count_;
timer1_.expires_at(timer1_.expiry() + boost::asio::chrono::seconds(1));
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print1, this)));
}
}
void print2()
{
if (count_ <10)
{
std::cout <<"Timer 2: " < ++count_;
timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
}
private:
boost::asio::strand strand_;
boost::asio::steady_timer timer1_;
boost::asio::steady_timer timer2_;
int count_;
};
int main()
{
boost::asio::io_context io;
printer p(io);
boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
io.run();
t.join();
return 0;
}


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了一个程序,可以输出1000内能被3整除且个位数为6的所有整数。程序使用了循环和条件判断语句来筛选符合条件的整数,并将其输出。 ... [详细]
author-avatar
手机用户2502881937
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有