Boost.Asio Library

1. io_service

io_service是Boost::Asio命名空间内的核心对象,I/O service是一个用于访问操作系统资源的通道,并在提交I/O请求的程序和执行I/O请求的操作系统之间建立通信。io_service对象使用最频繁的方法是run(),它用于不断应对io_service对象需要处理的事件。因此它会阻塞,直到所有的事件处理完毕。
如果想让io_service对象在没有事件需要处理时,run()依然阻塞,可以使用work()方法。例如:

#include <boost/asio.hpp>
#include <iostream>
int main(void) {
	boost::asio::io_service io_svc;
	boost::asio::io_service::work worker(io_svc); // 相当于改变了io_svc的性质
	
	io_svc.run(); // 这个等待并不占用CPU资源
	
	std::cout << "We will not see this line in console window :(" << std::endl;
	return 0;
}

在上面的代码中,work类通知io_service对象它有工作要做,但我们没有定义工作是什么。因此,程序将被无限阻塞,所以最后一行代码不会输出。阻塞的原因是run()函数被调用了。
run()相对的是poll()方法。poll()方法用于处理就绪的程序,直到没有剩余的就绪的程序或直到io_service对象停止。但是,与run()函数相比,poll()函数不会阻塞程序,即使使用了work()方法。

2. 删除work对象

可以通过从io_service对象中移除工作对象来解除程序阻塞,但是为了移除工作对象本身,我们必须使用指向工作对象的指针。

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include<boost/chrono.hpp>
#include <iostream>

int main(void) {
	boost::asio::io_service io_svc;
	boost::shared_ptr<boost::asio::io_service::work> worker(
					new boost::asio::io_service::work(io_svc));
	worker.reset(); // 销毁指针,所有待处理的工作都将结束
	io_svc.run();
	std::cout << "We will not see this line in console window :(" << std::endl;
	return 0;
}

3. 处理多线程

一个io_service在多个线程中处理。

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <iostream>

boost::asio::io_service io_svc;
int a = 0;

void WorkerThread() {
	std::cout << ++a << ".\n";
	io_svc.run();
	std::cout << "End.\n";
}
int main(void) {
	boost::shared_ptr<boost::asio::io_service::work> worker(
						new boost::asio::io_service::work(io_svc));
	std::cout << "Press ENTER key to exit!" << std::endl;
	
	boost::thread_group threads;
	for(int i=0; i<5; i++)
		threads.create_thread(WorkerThread);
		
	std::cin.get();
	io_svc.stop(); // worker.reset();
	threads.join_all();
	return 0;
}

4. 拷贝io_service指针

其他需要注意的是io_service是不可拷贝的对象。但可以通过在shared_ptr指针中实例化io_service对象,使其可复制,这样我们就可以将它绑定到worker thread()方法中,作为线程处理函数使用。

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>

using namespace boost::asio;

void workThread(std::shared_ptr<io_service> iosvc, int counter) {
    std::cout << counter << std::endl;
    iosvc->run();
    std::cout << "End." << std::endl;
}
int main() {
    //io_service ios;
    auto io_svc = std::make_shared<io_service>();
    std::shared_ptr<io_service::work> worker(new io_service::work(*io_svc));

    std::cout << "Press ENTER key to exit!" << std::endl;

    boost::thread_group threads;
    for(int i=1; i<=5; i++) {
        threads.create_thread(boost::bind(&workThread, io_svc, i));
    }

    std::cin.get();

    worker.reset(); // io_svc->stop(); 是将所有的工作停止,即使没有做完。worker.reset();像是取消io_service对象的work属性

    threads.join_all();

    return 0;
}

5. 互斥锁

在上述几个多线程的例子中,运行是随机的。因为std::cout对象是一个全局对象,一次从不同的线程写入,可能会导致输出格式化问题。可以使用互斥锁来同步对任何全局数据或共享数据的访问。

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex global_stream_lock;

void WorkerThread(boost::shared_ptr<boost::asio::io_service> iosvc,
int counter) {
	global_stream_lock.lock();
	std::cout << counter << ".\n";
	global_stream_lock.unlock();
	iosvc->run();
	global_stream_lock.lock();
	std::cout << "End.\n";
	global_stream_lock.unlock();
}

int main(void) {
	boost::shared_ptr<boost::asio::io_service> io_svc(
								new boost::asio::io_service
);
	boost::shared_ptr<boost::asio::io_service::work> worker(
							new boost::asio::io_service::work(*io_svc)
 );
	std::cout << "Press ENTER key to exit!" << std::endl;
	boost::thread_group threads;
	for(int i=1; i<=5; i++)
		threads.create_thread(boost::bind(&WorkerThread, io_svc, i));
	std::cin.get();
	io_svc->stop();
	threads.join_all();
	return 0;
}

6. 向 I/O service分发工作

io_service中有两个方法可以给io_service分配任务:post()方法用于请求io_service对象在我们将所有工作排队后运行io_service对象对应的work,所以它不允许我们立即运行工作。而dispatch()方法也用于向io_service对象发出一个请求来运行io_service对象的工作,但它会立即执行工作,而不会排队。

post()

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex global_stream_lock;

void WorkerThread(boost::shared_ptr<boost::asio::io_service> iosvc,
int counter) {
	global_stream_lock.lock();
	std::cout << counter << ".\n";
	global_stream_lock.unlock();
	iosvc->run();
	global_stream_lock.lock();
	std::cout << "End.\n";
	global_stream_lock.unlock();
}
size_t fac(size_t n) {
	if ( n <= 1 ) {
		return n;
	}
	boost::this_thread::sleep(
	boost::posix_time::milliseconds(1000)
);
	return n * fac(n - 1);
}
void CalculateFactorial(size_t n) {
	global_stream_lock.lock();
	std::cout << "Calculating " << n << "! factorial" << std::endl;
	global_stream_lock.unlock();
	
	size_t f = fac(n);
	
	global_stream_lock.lock();
	std::cout << n << "! = " << f << std::endl;
	global_stream_lock.unlock();
}
int main(void) {
	boost::shared_ptr<boost::asio::io_service> io_svc(
								new boost::asio::io_service
 );
	boost::shared_ptr<boost::asio::io_service::work> worker(
							new boost::asio::io_service::work(*io_svc)
);
	global_stream_lock.lock();
	std::cout << "The program will exit once all work has finished." << std::endl;
	global_stream_lock.unlock();
	boost::thread_group threads;
	for(int i=1; i<=5; i++)
		threads.create_thread(boost::bind(&WorkerThread, io_svc, i));
	io_svc->post(boost::bind(CalculateFactorial, 5));
	io_svc->post(boost::bind(CalculateFactorial, 6));
	io_svc->post(boost::bind(CalculateFactorial, 7));
	worker.reset();
	threads.join_all();
	return 0;
}

在main函数中,使用post()函数将三个函数对象发布到io_service对象上。我们在初始化5个工作线程时将io_service对象指针分配到5个线程中。这样,因为我们在每个线程中调用io_service对象的run()函数,所以io_service对象的work将会运行post()方法发布的工作。

dispatch()

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>

using namespace boost::asio;

//boost::mutex global_lock;

void Dispatch(int i) {
    std::cout << "dispatch() function for i=" << i << std::endl;
}

void Post(int i) {
    std::cout << "post() function for i=" << i << std::endl;
}

void workerThread(std::shared_ptr<io_service> iosv) {
    std::cout << "Thread Start." <<std::endl;

    iosv->run();

    std::cout << "Thread Finish." << std::endl;
}

void running(std::shared_ptr<io_service> iosv) {
    for(int i=0; i<5; i++) {
        iosv->dispatch(boost::bind(&Dispatch, i));
        iosv->post(boost::bind(&Post, i));
        boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
    }
}

int main() {
    auto iosv =std::make_shared<io_service>();
    //io_service::work worker(*iosv);
    auto worker = std::make_shared<io_service::work>(*iosv); // make_shared<io_service::work>会调用work的构造函数

    boost::thread_group threads;
    threads.create_thread(boost::bind(&workerThread, iosv));

    iosv->post(boost::bind(&running, iosv));

    worker.reset();

    threads.join_all();

    return 0;
}

输出结果:

Thread Start.
dispatch() function for i=0
dispatch() function for i=1
dispatch() function for i=2
dispatch() function for i=3
dispatch() function for i=4
post() function for i=0
post() function for i=1
post() function for i=2
post() function for i=3
post() function for i=4
Thread Finish.

按照常规的理解输出的结果应该是dispatch()和post()交替执行,但是结果却是先执行dispatch()再执行post()。这是因为dispatch()分发的工作要求从当前工作线程立即调用,而post()必须等到工作线程的处理程序完成后才能被调用。换句话说,post()在工作线程有其他未决的事件时需要排队等待,等到处理程序完成执行后才能被允许执行。
就是说我们首先post了一个running工作,而在这个工作中,又dispatch和post了工作,那么workerThread所在的线程中首先处理running工作,当运行到dispatch时,虽然running工作没有完成,但是dispatch也要求立即运行,而post只是将自己的任务排入一个队列,当线程不忙时可以运行,忙时排队等待。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是浩浩子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值