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只是将自己的任务排入一个队列,当线程不忙时可以运行,忙时排队等待。