В чем преимущество стренда в boost asio?

Изучаю boost asio и узнаю класс под названием "strand", насколько я понимаю. Если есть только один io_service, связанный с определенной цепью, и отправьте дескриптор цепью.

пример ( отсюда)

boost::shared_ptr< boost::asio::io_service > io_service( 
    new boost::asio::io_service
);
boost::shared_ptr< boost::asio::io_service::work > work(
    new boost::asio::io_service::work( *io_service )
);
boost::asio::io_service::strand strand( *io_service );

boost::thread_group worker_threads;
for( int x = 0; x < 2; ++x )
{
    worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );
}

boost::this_thread::sleep( boost::posix_time::milliseconds( 1000 ) );

strand.post( boost::bind( &PrintNum, 1 ) );
strand.post( boost::bind( &PrintNum, 2 ) );
strand.post( boost::bind( &PrintNum, 3 ) );
strand.post( boost::bind( &PrintNum, 4 ) );
strand.post( boost::bind( &PrintNum, 5 ) );

Затем цепочка будет сериализовать выполнение обработчика для нас. Но в чем преимущества этого? Почему бы нам просто не создать единый поток (например, сделать x = 1 в цикле for), если мы хотим, чтобы задачи стали сериализованными?

1 ответ

Решение

Подумайте о системе, где один io_service управляет сокетами для сотен сетевых подключений. Чтобы иметь возможность распараллелить рабочую нагрузку, система поддерживает пул рабочих потоков, которые вызывают io_service::run,

Теперь большинство операций в такой системе могут выполняться параллельно. Но некоторые из них должны быть сериализованы. Например, вы, вероятно, не хотели бы, чтобы несколько операций записи в одном сокете происходили одновременно. Затем вы должны использовать одну цепочку для каждого сокета для синхронизации записей: записи в разные сокеты могут по-прежнему выполняться одновременно, тогда как записи в одни и те же сокеты будут сериализованы. Рабочие потоки не должны заботиться о синхронизации или других сокетах, они просто получают все io_service::run руки им.

Кто-то может спросить: почему мы не можем просто использовать мьютекс вместо синхронизации? Преимущество цепочки заключается в том, что рабочий поток не будет запланирован в первую очередь, если цепочка уже обрабатывается. С мьютексом рабочий поток получит обратный вызов, а затем заблокирует попытку блокировки, не позволяя потоку выполнять какую-либо полезную работу, пока мьютекс не станет доступным.

Я знаю, что он слишком старый, но надеюсь, что он поможет новым пользователям разобраться с примерами. Прочтите комментарии в коде

#define BOOST_DATE_TIME_NO_LIB
#define BOOST_REGEX_NO_LIB

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.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 << "Thread " << std::this_thread::get_id() << ", " << counter << " Start.\n";
    global_stream_lock.unlock();

    iosvc->run();

    global_stream_lock.lock();
    std::cout << "Thread " << counter << " End.\n";
    global_stream_lock.unlock();
}

void async_send_handler(int number) {
    std::cout << "Number: " << number << ", threadID: " << std::this_thread::get_id() << std::endl;
}

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)
    );

    boost::asio::io_service::strand strand(*io_svc);

    global_stream_lock.lock();
    std::cout << "The program will exit once all work has finished.\n";
    global_stream_lock.unlock();

    boost::thread_group threads;
    for( int i = 1; i <= 5; i++ )
        threads.create_thread(boost::bind(&WorkerThread, io_svc, i));

    boost::this_thread::sleep(boost::posix_time::milliseconds(500));

    // Imagine you are invoking async_send on tcp or udp socket several times
    // and you want the handlers of this async_send call to be invoked sequentially

    // This code is almost equal to calling handlers of socket.async_send.
    // The handlers are invoked concurently and the order might be arbitrary
    io_svc->post(boost::bind(&async_send_handler, 1));
    io_svc->post(boost::bind(&async_send_handler, 2));
    io_svc->post(boost::bind(&async_send_handler, 3));
    io_svc->post(boost::bind(&async_send_handler, 4));
    io_svc->post(boost::bind(&async_send_handler, 5));

    // This code will do what you exactly want;
    // It will execute the handlers sequentially in that order
    strand.post(boost::bind(&async_send_handler, 1));
    strand.post(boost::bind(&async_send_handler, 2));
    strand.post(boost::bind(&async_send_handler, 3));
    strand.post(boost::bind(&async_send_handler, 4));
    strand.post(boost::bind(&async_send_handler, 5));

    worker.reset();

    threads.join_all();

    return 0;
}
Другие вопросы по тегам