post 与 dispacth的区别
在了解asio的线程模型前,先了解post与dispacth的区别,看如下的官方文档的描述
- service.post(handler): This function guarantees that it returns
immediately after it has requested the io_service instance to invoke the
given function handler. The handler will be called later in one of the threads
that has called service.run().- service.dispatch(handler): This requests the io_service instance to
invoke the given function handler, but in addition, it can execute the handler
inside the function if the current thread has called service.run().
首先 post方法是一个异步方法,调用后会马上返回,它会向io_service请求分发任务,io_serivce会选择一个线程执行。
dispatch会直接在内部(dispatch方法内部,本线程)就执行被分发的方法。看一个例子
#include "boost/asio.hpp"
#include <thread>
#include <iostream>
#include <chrono>
#include <mutex>
void fB()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << std::this_thread::get_id()
<< " running B function" << std::endl;
}
void fA(boost::asio::io_service& ios)
{
static int selector = 0;
std::cout << std::this_thread::get_id()
<< " starting A function" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
if (++selector % 2) // 1
{
std::cout << std::this_thread::get_id()
<< " dispatching" << std::endl;
ios.dispatch(fB);
}
else // 2
{
std::cout << std::this_thread::get_id()
<< " posting" << std::endl;
ios.post(fB);
}
std::cout << std::this_thread::get_id()
<< " exiting A function" << std::endl;
}
typedef std::shared_ptr<std::thread> ThreadPtr;
boost::asio::io_service ios;
ThreadPtr threads[3];
void ThreadF()
{
ios.run();
}
bool startAFunction()
{
std::cout << "Enter a non-empty string to run A function" << std::endl;
std::string input;
getline(std::cin, input);
return input.length() == 0 ? false : true;
}
int main()
{
std::cout << "Main thread: " << std::this_thread::get_id() << std::endl;
//让io_service没有任务不会结束循环
boost::asio::io_service::work work(ios);
size_t count = 3;
//开启线程
for (int i = 0; i < count; ++i)
{
threads[i].reset(new std::thread(ThreadF));
}
while (startAFunction())
ios.post(std::bind(fA, std::ref(ios)));
//threads.join_all();
for (int i = 0; i < count; ++i)
{
threads[i]->join();
}
std::cout << "All threads terminated" << std::endl;
std::system("pause");
}
在上面的程序,是多个线程调用同一个ios_service的run方法,在fA方法中,当selector值为单数时,调用的是dispatch方法,此时可以看到分发的任务fB在fA的同一个线程中执行,当为双数时调用的是post方法,分发的任务fB会分发到调用run方法的某一个线程中。
上面的程序,是有多个线程去调用io_service的run方法,这些线程组个的一个线程池,post方法分发的任务,会被io_service分发到线程池中的某一个线程中去。
所以io_service对象的线程模型体现在对io_service对象的run方法调用形式,不同的调用形式,会影响io_service post和dispatch方法对任务的分发方式。
io_service的线程模型
- 一个io_service对象,一个线程调用该对象的run方法
#include <iostream>
#include <thread>
#include "boost/asio.hpp"
using namespace boost::asio;
void func()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "thread id "<<std::this_thread::get_id()<< " this is func" << std::endl;
}
int main()
{
std::cout << " main thread " << std::this_thread::get_id() << std::endl;
io_service io;
io.post(func); //语句1
io.run(); //语句2
std::system("pause");
}
语句1向io_service分配一个任务,语句2在主线程中运行run方法,此时asio会将func分发到主线程中运行。这种情况下,post方法分发的任务,会被分发到执行io_service run方法的线程中(例子中是主线程)。
- 多个io_service对象,每个线程一个
#include <iostream>
#include <thread>
#include <memory>
#include "boost/asio.hpp"
using namespace boost::asio;
typedef std::shared_ptr<std::thread> ThreadPtr;
typedef std::shared_ptr<io_service> IOPtr;
void EventThread1(const IOPtr& io)
{
std::cout << "EventThread1,thread id:" << std::this_thread::get_id() << std::endl;
io->run();
}
void EventThread2(const IOPtr& io)
{
std::cout << "EventThread2,thread id:" << std::this_thread::get_id() << std::endl;
io->run();
}
void EventThread3(const IOPtr& io)
{
std::cout << "EventThread3,thread id:" << std::this_thread::get_id() << std::endl;
io->run();
}
void TaskF1()
{
std::cout << "thread id "<<std::this_thread::get_id()<<" TaskF1"<< std::endl;
}
void TaskF2()
{
std::cout << "thread id "<<std::this_thread::get_id()<<" TaskF2" << std::endl;
}
void TaskF3()
{
std::cout << "thread id "<<std::this_thread::get_id()<<" TaskF3" << std::endl;
}
//三个线程
ThreadPtr threads[3];
//三个io_service对象
IOPtr ios[3];
int main()
{
for (int i = 0; i < 3; ++i)
{
ios[i].reset(new io_service);
}
std::cout << "main thread " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
ios[0]->post(TaskF1); //语句1
std::this_thread::sleep_for(std::chrono::seconds(1));
ios[1]->post(TaskF2); //语句2
std::this_thread::sleep_for(std::chrono::seconds(1));
ios[2]->post(TaskF3); //语句3
std::this_thread::sleep_for(std::chrono::seconds(1));
threads[0].reset(new std::thread(std::bind(EventThread1, ios[0])));
std::this_thread::sleep_for(std::chrono::seconds(1));
threads[1].reset(new std::thread(std::bind(EventThread2, ios[1])));
std::this_thread::sleep_for(std::chrono::seconds(1));
threads[2].reset(new std::thread(std::bind(EventThread3, ios[2])));
std::system("pause");
}
有三个线程,每个线程分配了一个io_service对象,语句1 2 3分别给三个io_service对象分配任务,每个任务在本线程(调用io_serivce run方法的线程)中的执行。
3.多个线程运行一个io_service对象的run方法
这种模式下,io_service会建立一个线程池,线程池中的线程就是调用run方法的线程,任务会分发给线程池中的某一个空闲的线程
#include "boost/asio.hpp"
#include <thread>
#include <iostream>
#include <chrono>
#include <mutex>
void fA()
{
std::cout << std::this_thread::get_id() << " running A function" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(10)); //语句8
std::cout << "end fA" << std::endl;
}
void fB()
{
//std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << std::this_thread::get_id()
<< " running B function" << std::endl;
}
void fC()
{
std::cout << std::this_thread::get_id() << " running C function" << std::endl;
}
typedef std::shared_ptr<std::thread> ThreadPtr;
boost::asio::io_service ios;//语句1
ThreadPtr threads[2];//语句2
void ThreadF()
{
ios.run();
}
int main()
{
std::cout << "Main thread: " << std::this_thread::get_id() << std::endl;
//让io_service没有任务不会结束循环
boost::asio::io_service::work work(ios); //语句3
size_t count = 2;
//开启线程
for (int i = 0; i < count; ++i)
{
threads[i].reset(new std::thread(ThreadF)); //语句4
}
std::this_thread::sleep_for(std::chrono::seconds(1)); //语句5
ios.post(fA);//语句6
std::this_thread::sleep_for(std::chrono::seconds(1));
ios.post(fB);//语句7
std::this_thread::sleep_for(std::chrono::seconds(1));
ios.post(fC);
std::this_thread::sleep_for(std::chrono::seconds(3));
//std::cout << "Stopping ASIO I/O Service ..." << std::endl;
//ios.stop();
//threads.join_all();
for (int i = 0; i < count; ++i)
{
threads[i]->join();
}
std::cout << "All threads terminated" << std::endl;
std::system("pause");
}
语句 1定义一个io_service对象,语句2定义了包含3个thread对象的数组表示有2个线程,语句4 三个线程都执行同一个io_service对象的run方法,语句5 每隔1秒钟pos一个任务,定义了三个函数fA(),fB(),fC()表示不同的三个任务,语句6,7分别post了fA和fB,可以根据输出看到fA,fB分配到了不同线程去执行。改变语句8的 sleep时长来观察fC所分配到的线程。如果sleep时间很短或者是不sleep,会发现fA和fB被分配到了同一个线程去处理(sleep的时间短或不sleep表示为该线程是空闲的,io_service会将后续任务分配至该线程)。因为在这种模式下,任务分配到不同线程执行,所以对共享数据需要进行互斥。
参考: