[MSVC2010] Active object
От: Abyx Россия  
Дата: 23.06.11 18:27
Оценка:
Давайте подумаем как реализовать паттерн Active Object в С++ (точнее в С++0x в рамках MSVC2010)

Реализация должна быть эффективна, проста и универсальна.

Также надо придумать как измерять производительность того или иного решения.
Я вижу следующий сценарий работы Active Object'а — в основном потоке у него быстро и часто раз вызываются методы, в фоновом потоке они выполняются.
Соответственно получаются два критерия — время добавления метода в очередь, и время выполнения очереди.

Для начала — две штуки на IOCP и asio (из буста 1.46.1)

#1
// iocp_ActiveObject.h

#pragma once

#include <cassert>
#include <functional>
#include <process.h>
#include <Windows.h>

namespace iocp_based {

class ActiveObject
{
public:
    ActiveObject()
    {
        iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
        assert(iocp != 0 && "ActiveObject ctor");
        hThread = (HANDLE)_beginthreadex(nullptr, 0, threadFn, this, 0, nullptr);
        assert(hThread != 0 && "ActiveObject ctor");
    }
    ~ActiveObject()
    {
        if(hThread != 0)
            stop();
    }

    template<typename F>
    void post(F&& f)
    {
        auto fn = new std::function<void()>(std::forward<F>(f));
        ::PostQueuedCompletionStatus(iocp, 0, 0, (LPOVERLAPPED)fn);
    }

protected:
    void stop()
    {
        ::PostQueuedCompletionStatus(iocp, 0, 1, 0);
        ::WaitForSingleObject(hThread, INFINITE);
        ::CloseHandle(hThread);
        hThread = 0;
        ::CloseHandle(iocp);
        iocp = 0;
    }

    virtual void on_exception(std::exception*)
    {
        assert(!"Unhandled exception in ActiveObject");
    }

private:
    ActiveObject(const ActiveObject&);
    void operator=(const ActiveObject&);

    static unsigned __stdcall threadFn(void* param)
    {
        ((ActiveObject*)param)->threadMethod();
        return 0;
    }

    void threadMethod()
    {
        for(;;)
        {
            DWORD unused1;
            ULONG_PTR terminated;
            LPOVERLAPPED ptr;
            auto ok = ::GetQueuedCompletionStatus(iocp, &unused1, &terminated, &ptr, INFINITE);
            assert(ok && "ActiveObject GetQueuedCompletionStatus() failed");

            if(terminated != 0)
                return;

            auto f = (std::function<void()>*)ptr;
            try
            {
                (*f)();
            }
            catch(std::exception& e)
            {
                on_exception(&e);
            }
            catch(...)
            {
                on_exception(nullptr);
            }
            delete f;
        }
    }

    HANDLE hThread;
    HANDLE iocp;
};

} // ns


#2
// asio_win7_ActiveObject.h

#define _WIN32_WINNT 0x0601 // Win7 (WinXP yields same results)
#include <memory>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

namespace asio_win7 {

    class ActiveObject
    {
    public:
        ActiveObject()
            : ios()
            , work(new boost::asio::io_service::work(ios))
            , terminated(false)
        {
            thread.reset(new boost::thread([this]{ threadMethod(); }));
        }
        ~ActiveObject()
        {
            if(!terminated)
                stop();
        }

        template<typename F>
        void post(F&& f)
        {
            ios.post(f);
        }

    protected:
        void stop()
        {
            post([this]{ terminated = true; });
            work = nullptr;
            thread->join();
        }

        virtual void on_exception(std::exception*)
        {
            assert(!"Unhandled exception in ActiveObject");
        }

    private:
        ActiveObject(const ActiveObject&);
        void operator=(const ActiveObject&);

        void threadMethod()
        {
            while(!terminated)
            {
                try
                {
                    ios.run();
                }
                catch(std::exception& e)
                {
                    on_exception(&e);
                }
                catch(...)
                {
                    on_exception(nullptr);
                }
            }
        }

        boost::asio::io_service ios;
        std::unique_ptr<boost::asio::io_service::work> work;
        std::unique_ptr<boost::thread> thread;
        bool terminated;
    };

}


и код для тестирования
#include "asio_win7_ActiveObject.h"
#include "iocp_ActiveObject.h"

#include <iostream>
#include <intrin.h>
#include <Windows.h>

const auto TEST_CYCLES = 20;
const auto POST_CYCLES = 200;

template<typename ActiveObject>
__declspec(noinline)
void run(ActiveObject& ao)
{
    ao.post([](){});
}

template<typename ActiveObject>
__int64 measure(ActiveObject& ao)
{
    __int64 total = 0;
    for(auto i = POST_CYCLES; i != 0; --i)
    {
        ::Sleep(0);
        auto start = __rdtsc();

        run(ao);

        auto stop = __rdtsc();
        total += stop - start;
    }
    return total;
}

template<typename ActiveObject>
void test(ActiveObject& ao)
{
    for(auto i = TEST_CYCLES; i != 0; --i)
    {
        auto eplased = measure(ao);
        volatile bool finished = false;

        std::cout << (eplased / POST_CYCLES) << ", ";

        ao.post([&](){ finished = true; });
        do ::Sleep(10); while(!finished);
    }

    std::cout << '\n' << std::endl;
}

int main()
{
    ::SetThreadAffinityMask(GetCurrentThread(), 1);

    {
        std::cout << "ASIO Win7" << std::endl;
        asio_win7::ActiveObject ao;
        test(ao);
    }
    {
        std::cout << "IOCP" << std::endl;
        iocp_based::ActiveObject ao;
        test(ao);
    }
}


логично, что производительность у них одинаковая 2000-5000 тактов, однако разброс огромен, в т.ч. между запусками приложения
In Zen We Trust
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.