io_service对象的线程模型

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的线程模型

  1. 一个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方法的线程中(例子中是主线程)。

  1. 多个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会将后续任务分配至该线程)。因为在这种模式下,任务分配到不同线程执行,所以对共享数据需要进行互斥。

参考:

post与dispatch的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

mo4776

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

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

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

打赏作者

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

抵扣说明:

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

余额充值