发布后重新排序提升处理程序

时间:2016-02-04 18:23:55

标签: multithreading boost boost-asio

我想重新排序由boost io_service处理的处理程序:

这是我的伪代码:

start()
{
   io.run();
}

thread1()
{
   io.post(myhandler1);
}

thread2()
{
   io.post(myhandler2);
}

thread1()和thread2()是独立调用的。

在这种情况下,io_service按照发布顺序处理处理程序。

队列示例:myhandler1 | myhandler1 | myhandler2 | myhandler1 | myhandler2

如何修改io_service处理顺序以一个接一个地执行myhandler1和myhandler2?

新队列示例:myhandler1 | myhandler2 | myhandler1 | myhandler2 | myhandler1

我写了这段代码,但CPU使用率是100%:

start()
{
   while(1)
   {
     io1.poll_one();
     io2.poll_one();
   }
}

thread1()
{
   io1.post(myhandler1);
}

thread2()
{
   io2.post(myhandler2);
}

由于

2 个答案:

答案 0 :(得分:1)

我会使用两个队列。从这个ASIO anwer我做了一次(Non blocking boost io_service for deadline_timers)我参加了thread_pool课程。

我将其拆分为task_queuethread_pool类。

我创建了一个知道如何兼顾两个队列的worker类型:

struct worker {
    task_queue q1, q2;

    void wake() {
        q1.wake();
        q2.wake();
    }

    void operator()(boost::atomic_bool& shutdown) {
        std::cout << "Worker start\n";
        while (true) {
            auto job1 = q1.dequeue(shutdown);
            if (job1) (*job1)();

            auto job2 = q2.dequeue(shutdown);
            if (job2) (*job2)();

            if (shutdown && !(job1 || job2))
                break;
        }
        std::cout << "Worker exit\n";
    }
};

您可以看到工作循环的结构如何 - 如果任务已入队 - 将轮流提供队列。

  

注意:wake()调用是可靠的关闭;队列使用阻塞等待,因此当切换shutdown标志时,需要发信号通知(唤醒)。

完整演示

<强> Live On Coliru

#include <boost/function.hpp>
#include <boost/optional.hpp>
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
#include <iostream>
#include <deque>

namespace custom {
    using namespace boost;

    class task_queue {
    private:
        mutex mx;
        condition_variable cv;

        typedef function<void()> job_t;
        std::deque<job_t> _queue;

    public:
        void enqueue(job_t job)
        {
            lock_guard<mutex> lk(mx);
            _queue.push_back(job);

            cv.notify_one();
        }

        template <typename T>
        optional<job_t> dequeue(T& shutdown)
        {
            unique_lock<mutex> lk(mx);

            cv.wait(lk, [&] { return shutdown || !_queue.empty(); });

            if (_queue.empty())
                return none;

            job_t job = _queue.front();
            _queue.pop_front();
            return job;
        }

        void wake() {
            lock_guard<mutex> lk(mx);
            cv.notify_all();
        }
    };

    template <typename Worker> class thread_pool
    {
    private:
        thread_group       _pool;
        boost::atomic_bool _shutdown { false };
        Worker             _worker;

        void start() {
            for (unsigned i = 0; i < 1 /*boost::thread::hardware_concurrency()*/; ++i){
                std::cout << "Creating thread " << i << "\n";
                _pool.create_thread([&] { _worker(_shutdown); });
            }
        }
    public:
        thread_pool() { start(); }

        ~thread_pool() {
            std::cout << "Pool going down\n";
            _shutdown = true;
            _worker.wake();
            _pool.join_all();
        }

        Worker& get_worker() { return _worker; }
    };

    struct worker {
        task_queue q1, q2;

        void wake() {
            q1.wake();
            q2.wake();
        }

        void operator()(boost::atomic_bool& shutdown) {
            std::cout << "Worker start\n";
            while (true) {
                auto job1 = q1.dequeue(shutdown);
                if (job1) (*job1)();

                auto job2 = q2.dequeue(shutdown);
                if (job2) (*job2)();

                if (shutdown && !(job1 || job2))
                    break;
            }
            std::cout << "Worker exit\n";
        }
    };
}

void croak(char const* queue, int i) {
    static boost::mutex cout_mx;
    boost::lock_guard<boost::mutex> lk(cout_mx);
    std::cout << "thread " << boost::this_thread::get_id() << " " << queue << " task " << i << "\n"; 
}

int main() {
    custom::thread_pool<custom::worker> pool;

    auto& queues = pool.get_worker();
    for (int i = 1; i <= 10; ++i) queues.q1.enqueue([i] { croak("q1", i); });
    for (int i = 1; i <= 10; ++i) queues.q2.enqueue([i] { croak("q2", i); });
}

打印例如。

Creating thread 0
Pool going down
Worker start
thread 7f7311397700 q1 task 1
thread 7f7311397700 q2 task 1
thread 7f7311397700 q1 task 2
thread 7f7311397700 q2 task 2
thread 7f7311397700 q1 task 3
thread 7f7311397700 q2 task 3
thread 7f7311397700 q1 task 4
thread 7f7311397700 q2 task 4
thread 7f7311397700 q1 task 5
thread 7f7311397700 q2 task 5
thread 7f7311397700 q1 task 6
thread 7f7311397700 q2 task 6
thread 7f7311397700 q1 task 7
thread 7f7311397700 q2 task 7
thread 7f7311397700 q1 task 8
thread 7f7311397700 q2 task 8
thread 7f7311397700 q1 task 9
thread 7f7311397700 q2 task 9
thread 7f7311397700 q1 task 10
thread 7f7311397700 q2 task 10
Worker exit

概括

这里推广了更多队列(例如三个):

<强> Live On Coliru

注意上面有1个工作线程服务;如果您创建了多个线程,则每个线程单独将在队列之间交替,但总体而言,订单将是未定义的(因为线程调度未定义)。

这里的通用版本有点更准确,因为它在工作线程之间共享idx变量,但实际的输出顺序仍取决于线程调度。

答案 1 :(得分:0)

使用run_one()代替poll_one()应该有用(请注意,reset()也是必需的):

start()
{
   while(1)
   {
     io1.run_one();
     io2.run_one();

     io1.reset();
     io2.reset();
   }
}

但是,我不知道这是否是您可能遇到的任何实际问题的解决方案。这是问题的一个例子,&#34;你真正想做什么?&#34;似乎相关。例如,如果在每次调用handler2后运行handler1是有意义的,那么handler1可能会调用handler2