要向 Boost.Asio 中增加新的异步操作,需要实现以下三个类:
一个派生自 的类,以表示新的 I/O 对象。使用这个新的 Boost.Asio 扩展的开发者将只会看到这个 I/O 对象。
一个派生自
boost::asio::io_service::service
的类,表示一个服务,它被注册为 I/O 服务,可以从 I/O 对象访问它。 服务与 I/O 对象之间的区别是很重要的,因为在任意给定的时间点,每个 I/O 服务只能有一个服务实例,而一个服务可以被多个 I/O 对象访问。一个不派生自任何其它类的类,表示该服务的具体实现。 由于在任意给定的时间点每个 I/O 服务只能有一个服务实例,所以服务会为每个 I/O 对象创建一个其具体实现的实例。 该实例管理与相应 I/O 对象有关的内部数据。
本节中开发的 Boost.Asio 扩展并不仅仅提供一个框架,而是模拟一个可用的 boost::asio::deadline_timer
对象。 它与原来的 boost::asio::deadline_timer
的区别在于,计时器的时长是作为参数传递给 wait()
或 async_wait()
方法的,而不是传给构造函数。
每个 I/O 对象通常被实现为一个模板类,要求以一个服务来实例化 - 通常就是那个特定为此 I/O 对象开发的服务。 当一个 I/O 对象被实例化时,该服务会通过父类 boost::asio::basic_io_object
自动注册为 I/O 服务,除非它之前已经注册。 这样可确保任何 I/O 对象所使用的服务只会每个 I/O 服务只注册一次。
一般一上谕,I/O 对象是相对简单的:服务的安装以及服务实现的创建都是由父类 boost::asio::basic_io_object
来完成的,方法调用则只是前转至相应的服务;以 I/O 对象的实际服务实现作为参数即可。
为了与 Boost.Asio 集成,一个服务必须符合几个要求:
它必须派生自
boost::asio::io_service::service
。 构造函数必须接受一个指向 I/O 服务的引用,该 I/O 服务会被相应地传给boost::asio::io_service::service
的构造函数。任何服务都必须包含一个类型为
boost::asio::ioservice::id
的静态公有属性 _id。在 I/O 服务的内部是用该属性来识别服务的。必须定义一个名为
shutdown_service()
的方法;不过它可以是私有的。 对于一般的 Boost.Asio 扩展来说,它通常是一个空方法。 只有与 Boost.Asio 集成得非常紧密的服务才会使用它。 但是这个方法必须要有,这样扩展才能编译成功。
在线程的协助下使用异步操作,通常是通过访问一个新的 I/O 服务来完成的。 上述例子中包含了一个名为 asyncioservice_ 的属性,其类型为 boost::asio::io_service
。 这个 I/O 服务的 run()
方法是在它自己的线程中启动的,而它的线程是在该服务的构造函数内部由类型为 boost::thread
的 _async_thread 创建的。 第三个属性 _async_work 的类型为 boost::scoped_ptr<boost::asio::io_service::work>
,用于避免 run()
方法立即返回。 否则,这可能会发生,因为已没有其它的异步操作在创建。 创建一个类型为 boost::asio::io_service::work
的对象并将它绑定至该 I/O 服务,这个动作也是发生在该服务的构造函数中,可以防止 run()
方法立即返回。
一个服务也可以无需访问它自身的 I/O 服务来实现 - 单线程就足够的。 为新增的线程使用一个新的 I/O 服务的原因是,这样更简单: 线程间可以用 I/O 服务来非常容易地相互通信。 在这个例子中,async_wait()
创建了一个类型为 wait_operation
的函数对象,并通过 post()
方法将它传递给内部的 I/O 服务。 然后,在用于执行这个内部 I/O 服务的 run()
方法的线程内,调用该函数对象的重载 operator()()
。 提供了一个简单的方法,在另一个线程中执行一个函数对象。
waitoperation
的重载 operator()()
操作符基本上就是执行了和 wait()
方法相同的工作:调用服务实现中的阻塞式 wait()
方法。 但是,有可能这个 I/O 对象以及它的服务实现在这个线程执行 operator()()
操作符期间被销毁。 如果服务实现是在 destruct()
中销毁的,则 operator()()
操作符将不能再访问它。 这种情形是通过使用一个弱指针来防止的,从第一章中我们知道:如果在调用 lock()
时服务实现仍然存在,则弱指针 impl 返回它的一个共享指针,否则它将返回0。 在这种情况下,operator()()
不会访问这个服务实现,而是以一个 boost::asio::error::operation_aborted
错误来调用句柄。
服务实现 timer_impl
使用了 Windows API 函数,只能在 Windows 中编译和使用。 这个例子的目的只是为了说明一种潜在的实现。
timer_impl
提供两个基本方法:wait()
用于等待数秒。 destroy()
则用于取消一个等待操作,这是必须要有的,因为对于异步操作来说,wait()
方法是在其自身的线程中调用的。 如果 I/O 对象及其服务实现被销毁,那么阻塞式的 wait()
方法就要尽使用 destroy()
来取消。
这个 Boost.Asio 扩展可以如下使用。
目录监视器(Directory Monitor) 是现实中的一个 Boost.Asio 扩展,它提供了一个可以监视目录的 I/O 对象。 如果被监视目录中的某个文件被创建、修改或是删除,就会相应地调用一个句柄。 当前的版本支持 Windows 和 Linux (内核版本 2.6.13 或以上)。