Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Customization

Boost , Chapter 1. Fiber , Chapter 1. Fiber

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Overview

Как отмечено в разделе Расписание, по умолчанию Boost.Fiber использует собственный планировщик round_robin для каждого потока. Чтобы контролировать путь Boost.Fiber закладывает готовые волокна на определенную нить, в общем необходимо выполнить несколько шагов. В этом разделе рассматриваются эти этапы, в то время как Расписание служит ориентиром для соответствующих классов.

Диспетчер волокон библиотеки отслеживает взвешенные (блокированные) волокна. Только когда волокно становится готовым к запуску, оно передается планировщику. Конечно, если есть менее двух готовых волокон, работа планировщика тривиальна. Только когда есть два или более готовых волокон, конкретная реализация планировщика начинает влиять на общую последовательность выполнения волокна.

В этом разделе мы иллюстрируем простой пользовательский планировщик, который соблюдает целочисленный приоритет волокна. Мы реализуем его таким образом, чтобы волокно с более высоким приоритетом было предпочтительнее волокна с более низким приоритетом. Любые волокна с равными приоритетными значениями обслуживаются на кругло-робиновой основе.

Полный исходный код для приведенных ниже примеров находится в priority.cpp.

Custom Property Class

The first essential point is that we must associate an integer priority with each fiber.[9]

Можно предложить создать пользовательский подкласс fiber для хранения таких свойств. Есть несколько причин для нынешнего механизма.

  1. Boost.Fiber предоставляет ряд различных способов запуска волокна. (См. волокна::async().) Библиотеки более высокого уровня могут вводить дополнительные функции обертки. Пользовательский планировщик должен ассоциировать свои пользовательские свойства с каждым волокном в потоке, а не только с теми, которые явно запущены путем инстанцирования пользовательского подкласса fiber.
  2. Рассмотрим большую существующую программу, которая запускает волокна в разных местах кода. Мы обнаруживаем необходимость ввести пользовательский планировщик для конкретной нити. Если поддержка пользовательских свойств планировщика требует определенного подкласса fiber, нам придется искать и изменять каждое место, которое запускает волокно на этой нити.
  3. Класс fiber на самом деле является просто ручкой для внутренних данных context. Подкласс fiber не добавит данные в контекст.

Настоящий механизм позволяет drop в пользовательский планировщик с его сопутствующими пользовательскими свойствами без изменения остальной части вашего приложения.

Вместо того, чтобы получать пользовательский подкласс свойств волокна планировщика из волокна, вы должны вместо этого вывести его из волокна_свойств.

class priority_props : public boost::fibers::fiber_properties {
public:
    priority_props( boost::fibers::context * ctx):
        fiber_properties( ctx), 1
        priority_( 0) {
    }
    int get_priority() const {
        return priority_; 2
    }
    // Call this method to alter priority, because we must notify
    // priority_scheduler of any change.
    void set_priority( int p) { 3
        // Of course, it's only worth reshuffling the queue and all if we're
        // actually changing the priority.
        if ( p != priority_) {
            priority_ = p;
            notify();
        }
    }
    // The fiber name of course is solely for purposes of this example
    // program; it has nothing to do with implementing scheduler priority.
    // This is a public data member -- not requiring set/get access methods --
    // because we need not inform the scheduler of any change.
    std::string name; 4
private:
    int priority_;
};

1

fiber_properties конструктор. .

2

Предоставьте методы доступа к чтению по своему усмотрению.

3

Важно позвонить уведомить () о любом изменении свойства, которое может повлиять на поведение планировщика. Поэтому такие изменения должны выполняться только с помощью метода доступа.

4

Свойство, которое не влияет на планировщик, не нуждается в методах доступа.

Custom Scheduler Class

Теперь мы можем вывести пользовательский планировщик из algorithm_with_properties<>, указав в качестве параметра шаблона наш пользовательский класс свойств priority_props.

class priority_scheduler :
    public boost::fibers::algo::algorithm_with_properties< priority_props > {
private:
    typedef boost::fibers::scheduler::ready_queue_t1   rqueue_t;
    rqueue_t                                rqueue_;
    std::mutex                  mtx_{};
    std::condition_variable     cnd_{};
    bool                        flag_{ false };
public:
    priority_scheduler() :
        rqueue_() {
    }
    // For a subclass of algorithm_with_properties<>, it's important to
    // override the correct awakened() overload.
    2virtual void awakened( boost::fibers::context * ctx, priority_props & props) noexcept {
        int ctx_priority = props.get_priority(); 3
        // With this scheduler, fibers with higher priority values are
        // preferred over fibers with lower priority values. But fibers with
        // equal priority values are processed in round-robin fashion. So when
        // we're handed a new context*, put it at the end of the fibers
        // with that same priority. In other words: search for the first fiber
        // in the queue with LOWER priority, and insert before that one.
        rqueue_t::iterator i( std::find_if( rqueue_.begin(), rqueue_.end(),
            [ctx_priority,this]( boost::fibers::context & c)
            { return properties( &c ).get_priority() < ctx_priority; }));
        // Now, whether or not we found a fiber with lower priority,
        // insert this new fiber here.
        rqueue_.insert( i, * ctx);
    }
    4virtual boost::fibers::context * pick_next() noexcept {
        // if ready queue is empty, just tell caller
        if ( rqueue_.empty() ) {
            return nullptr;
        }
        boost::fibers::context * ctx( & rqueue_.front() );
        rqueue_.pop_front();
        return ctx;
    }
    5virtual bool has_ready_fibers() const noexcept {
        return ! rqueue_.empty();
    }
    6virtual void property_change( boost::fibers::context * ctx, priority_props & props) noexcept {
        // Although our priority_props class defines multiple properties, only
        // one of them (priority) actually calls notify() when changed. The
        // point of a property_change() override is to reshuffle the ready
        // queue according to the updated priority value.
        // 'ctx' might not be in our queue at all, if caller is changing the
        // priority of (say) the running fiber. If it's not there, no need to
        // move it: we'll handle it next time it hits awakened().
        if ( ! ctx->ready_is_linked()) { 7
            return;
        }
        // Found ctx: unlink it
        ctx->ready_unlink();
        // Here we know that ctx was in our ready queue, but we've unlinked
        // it. We happen to have a method that will (re-)add a context* to the
        // right place in the ready queue.
        awakened( ctx, props);
    }
    void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept {
        if ( (std::chrono::steady_clock::time_point::max)() == time_point) {
            std::unique_lock< std::mutex > lk( mtx_);
            cnd_.wait( lk, [this](){ return flag_; });
            flag_ = false;
        } else {
            std::unique_lock< std::mutex > lk( mtx_);
            cnd_.wait_until( lk, time_point, [this](){ return flag_; });
            flag_ = false;
        }
    }
    void notify() noexcept {
        std::unique_lock< std::mutex > lk( mtx_);
        flag_ = true;
        lk.unlock();
        cnd_.notify_all();
    }
};

1

См. ready_queue_t.

2

Вы должны переопределить метод algorithm_with_properties::awakened(). Вот как ваш планировщик получает уведомление о волокне, которое стало готовым к запуску.

3

props является экземпляром priority_props, связанным с пройденным волокном ctx.

4

Вы должны переопределить метод algorithm_with_properties::pick_next(). Вот как ваш планировщик фактически советует менеджеру волокна следующего волокна работать.

5

Вы должны переопределить algorithm_with_properties::has_ready_fibers(), чтобы сообщить диспетчеру волокон о состоянии вашей готовой очереди.

6

Переопределение алгоритм_с_свойствами::property_change() необязательно. Это переопределение обрабатывает случай, в котором работающее волокно меняет приоритет другого готового волокна: волокна, уже находящегося в нашей очереди. В этом случае переместите обновленное волокно в очередь.

7

Ваш perty_change() override должен быть в состоянии справиться с случаем, когда пройденный ctx не находится в вашей готовой очереди. Он может быть запущен, или он может быть заблокирован.

algorithm_with_properties::new_properties(): priority_props экземпляры на кучи. .

Replace Default Scheduler

Вы должны позвонить use_scheduling_algorithm() в начале каждого потока, на котором вы хотите Boost.Fiber использовать свой собственный планировщик, а не свой собственный по умолчанию round_robin. В частности, вы должны позвонить use_scheduling_algorithm(), прежде чем выполнять какие-либо другие операции Boost.Fiber.

int main( int argc, char *argv[]) {
    // make sure we use our priority_scheduler rather than default round_robin
    boost::fibers::use_scheduling_algorithm< priority_scheduler >();
    ...
}

Use Properties

Работающее волокно может получить доступ к своему собственному экземпляру подкласса fiber_properties, позвонив this_fiber::properties().Хотя properties<>() является нулевой функцией, вы должны пройти, как параметр шаблона, подкласс fiber_properties.

boost::this_fiber::properties< priority_props >().name = "main";

Учитывая экземпляр волокно, все еще связанный с работающим волокном (то есть не волокно::detach()ed), вы можете получить доступ к свойствам этого волокна, используя волокно::свойства(). Как и в случае с this_fiber::свойства<>(), вы должны пройти подкласс fiber_свойства в качестве параметра шаблона.

template< typename Fn >
boost::fibers::fiber launch( Fn && func, std::string const& name, int priority) {
    boost::fibers::fiber fiber( func);
    priority_props & props( fiber.properties< priority_props >() );
    props.name = name;
    props.set_priority( priority);
    return fiber;
}

Запуск новых волоконных графиков, которые клетчатки готовы, но не сразу же вводят свою волокно-функцию. Токовое волокно сохраняет контроль до тех пор, пока не блокирует (или не дает, или не прекращает) по какой-либо другой причине. Как показано в описанной выше функции launch(), разумно запустить волокно и сразу же установить соответствующие свойства, такие как, например, его приоритет. Затем ваш пользовательский планировщик может использовать эту информацию в следующий раз, когда менеджер волокон позвонит algorithm_with_properties::pick_next().



[9] Предыдущая версия библиотеки Fiber неявно отслеживала внутренний приоритет для каждого волокна, хотя планировщик по умолчанию проигнорировал его. Это было отброшено, так как библиотека теперь поддерживает произвольные свойства календаря.


PrevUpHomeNext

Статья Customization раздела Chapter 1. Fiber Chapter 1. Fiber может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 1. Fiber ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 19:24:03/0.0094671249389648/0