Волокна в нити координируются диспетчером волокон. Контроль торговли волокнами осуществляется совместно, а не упреждающе: волокно, работающее в настоящее время, сохраняет контроль до тех пор, пока не вызовет некоторую операцию, которая передает контроль менеджеру. Каждый раз, когда волокно приостанавливается (или дает выход), менеджер волокна консультируется с планировщиком, чтобы определить, какое волокно будет работать дальше.
Boost.Fiber предоставляет диспетчер волокон, но планировщик является точкой настройки. (См. Customization.)
Каждая нить имеет свой планировщик. Различные потоки в процессе могут использовать разные планировщики. По умолчанию Boost.Fiber неявно представляет round_robin в качестве планировщика для каждого потока.
Вам явно разрешено кодировать свой собственный подкласс алгоритм. По большей части вашему подклассу алгоритм не нужно защищаться от поперечных вызовов: менеджер волокон перехватывает и откладывает такие вызовы. Большинство методов алгоритм напрямую вызываются только из потока, волокнами которого он управляет — за исключением случаев, описанных ниже.
Ваш подкласс алгоритм включается в конкретную нить, позвонив use_scheduling_algorithm():
void thread_fn() {
boost::fibers::use_scheduling_algorithm< my_fiber_scheduler >();
...
}
Класс планировщика должен реализовывать интерфейс алгоритм. Boost.Fiber предоставляет один планировщик: round_robin.
алгоритм является абстрактным базовым классом, определяющим интерфейс, который должен реализовать планировщик волокон.
#include <boost/fiber/algo/algorithm.hpp>
namespace boost {
namespace fibers {
namespace algo {
struct algorithm {
virtual ~algorithm();
virtual void awakened( context *) noexcept = 0;
virtual context * pick_next() noexcept = 0;
virtual bool has_ready_fibers() const noexcept = 0;
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept = 0;
virtual void notify() noexcept = 0;
};
}}}
virtual void awakened( context * f) noexcept = 0;
- Effects:
Информирует планировщик, что волокно f готово к запуску. Волокно f может быть запущено недавно, или оно может быть заблокировано, но только что было пробуждено, или оно может называться this_fiber::yield().
- Note:
Этот метод советует планировщику добавить волокно f в свою коллекцию волокон, готовых к работе. Типичная реализация планировщика помещает f в очередь.
- See also:
round_robin
virtual context * pick_next() noexcept = 0;
- Returns:
волокно, которое должно быть возобновлено следующим образом, или nullptr, если нет готового волокна.
- Note:
Именно здесь планировщик фактически определяет волокно, которое будет работать дальше. Типичная реализация планировщика выбирает главу готовой очереди.
- See also:
round_robin
virtual bool has_ready_fibers() const noexcept = 0;
- Returns:
true, если планировщик имеет готовые к запуску волокна.
virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept = 0;
- Effects:
Информирует планировщик, что ни одно волокно не будет готово до точки времени abs_time.
- Note:
Этот метод позволяет пользовательскому планировщику передавать контроль в содержащую среду любым способом. Диспетчер волокон утверждает, что suspend_until() не должен возвращаться до тех пор, пока abs_time — или алгоритм::notify() не будет называться — в зависимости от того, что наступит раньше. Взаимодействие с notify()::sleep_until(abs_time) будет слишком упрощенным.round_robin::suspend_until() использует std:: для координации с round_robin::notify().
- Note:
Учитывая, что notify() может быть вызван из другого потока, ваша реализация suspend_until() — как и остальная часть вашей реализации algorithm — должна защищать любые данные, которыми она делится с вашей реализацией notify().
virtual void notify() noexcept = 0;
- Effects:
Просит планировщика вернуться с ожидающего вызова на алгоритм::suspend_until().
- Note:
Один из методов алгоритм, уведомить() можно вызвать из другого потока. Ваша реализация уведомляет () должна защищать любые данные, которыми она делится с остальной частью вашей реализации алгоритм.
Этот класс реализует алгоритм, планируя волокна в круговом режиме.
#include <boost/fiber/algo/round_robin.hpp>
namespace boost {
namespace fibers {
namespace algo {
class round_robin : public algorithm {
virtual void awakened( context *) noexcept;
virtual context * pick_next() noexcept;
virtual bool has_ready_fibers() const noexcept;
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept;
virtual void notify() noexcept;
};
}}}
virtual void awakened( context * f) noexcept;
- Effects:
Очереди волокна f на готовую очередь.
- Throws:
Ничего.
virtual context * pick_next() noexcept;
- Returns:
волокно во главе готовой очереди или nullptr, если очередь пуста.
- Throws:
Ничего.
- Note:
Размещая готовые волокна на хвосте очереди и возвращая их из головы этой очереди, делит нить между готовыми волокнами круговым способом.
virtual bool has_ready_fibers() const noexcept;
- Returns:
true, если планировщик имеет готовые к запуску волокна.
- Throws:
Ничего.
virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept;
virtual void notify() noexcept = 0;
Этот класс реализует алгоритм ; готовые волокна разделяются между всеми экземплярами (запущенными на разных нитях) Shared_work.
#include <boost/fiber/algo/shared_work.hpp>
namespace boost {
namespace fibers {
namespace algo {
class shared_work : public algorithm {
virtual void awakened( context *) noexcept;
virtual context * pick_next() noexcept;
virtual bool has_ready_fibers() const noexcept;
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept;
virtual void notify() noexcept;
};
}}}
virtual void awakened( context * f) noexcept;
- Effects:
Очереди волокна f на общую готовую очередь.
- Throws:
Ничего.
virtual context * pick_next() noexcept;
- Returns:
волокно во главе готовой очереди или nullptr, если очередь пуста.
- Throws:
Ничего.
- Note:
Размещая готовые волокна на хвосте общей очереди и возвращая их из головы этой очереди, делит нить между готовыми волокнами круговым способом.
virtual bool has_ready_fibers() const noexcept;
- Returns:
true, если планировщик имеет готовые к запуску волокна.
- Throws:
Ничего.
virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept;
virtual void notify() noexcept = 0;
Класс планировщика, непосредственно полученный из алгоритма, может использовать любую информацию, доступную из контекста, для реализации интерфейса алгоритма. Но пользовательскому планировщику может потребоваться отслеживать дополнительные свойства волокна. Например, планировщику, основанному на приоритете, необходимо будет отслеживать приоритет волокна & # 8217.
Boost.Fiber обеспечивает механизм, с помощью которого ваш пользовательский планировщик может связывать пользовательские свойства с каждым волокном.
Класс свойств волокна должен быть получен из fiber_properties.
#include <boost/fiber/properties.hpp>
namespace boost {
namespace fibers {
class fiber_properties {
public:
fiber_properties( context *) noexcept;
virtual ~fiber_properties();
protected:
void notify() noexcept;
};
}}
fiber_properties( context * f) noexcept;
- Effects:
Конструирует компонент базового класса пользовательского подкласса.
- Throws:
Ничего.
- Note:
Конструктор подкласса должен принять контекст* и передать его конструатору базового класса fiber_properties.
void notify() noexcept;
- Effects:
Передайте управление на заказ algorithm_with_properties<> subclass’s algorithm_with_properties::property_change() method.
- Throws:
Ничего.
- Note:
Пользовательский планировщик’s алгоритм_с_свойствами::pick_next() метод может динамически выбирать из готовых волокон, или алгоритм_с_свойствами::awakened() может вместо этого вставлять каждое готовое волокно в некоторую форму готовой очереди для pick_next(). В последнем случае, если код приложения изменяет свойство волокна (например, приоритет), которое должно повлиять на отношение волокна & # 8217 к другим готовым волокнам, пользовательскому планировщику должна быть предоставлена возможность переупорядочения его готовой очереди. Подкласс пользовательского свойства должен реализовать способ доступа для изменения такого свойства; этот метод доступа должен вызывать уведомлять () после того, как новое значение свойства было сохранено. Это передает управление пользовательскому планировщику’s property_change() методу, позволяя пользовательскому планировщику соответствующим образом переупорядочивать свою готовую очередь. Используйте по своему усмотрению. Конечно, если вы определяете свойство, которое не влияет на поведение метода pick_next(), вам не нужно звонить уведомлять () при изменении этого свойства.
Пользовательский планировщик, который зависит от класса пользовательских свойств PROPS, должен быть получен из algorithm_with_properties<PROPS>. PROPS должен быть получен из fiber_properties.
#include <boost/fiber/algorithm.hpp>
namespace boost {
namespace fibers {
namespace algo {
template< typename PROPS >
struct algorithm_with_properties {
virtual void awakened( context *, PROPS &) noexcept = 0;
virtual context * pick_next() noexcept;
virtual bool has_ready_fibers() const noexcept;
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept = 0;
virtual void notify() noexcept = 0;
PROPS & properties( context *) noexcept;
virtual void property_change( context *, PROPS &) noexcept;
virtual fiber_properties * new_properties( context *);
};
}}}
virtual void awakened( context * f, PROPS & properties) noexcept;
- Effects:
Информирует планировщик, что волокно f готово к запуску, как алгоритм::awakened(). Пропускает связанный с волокном’s экземпляр PROPS.
- Throws:
Ничего.
- Note:
Подкласс algorithm_with_properties<> должен переопределить этот метод вместо algorithm::awakened().
virtual context * pick_next() noexcept;
- Returns:
волокно, которое должно быть возобновлено следующим образом, или nullptr, если нет готового волокна.
- Throws:
Ничего.
- Note:
алгоритм::pick_next()
virtual bool has_ready_fibers() const noexcept;
virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept = 0;
- Effects:
Информирует планировщик, что ни одно волокно не будет готово до точки времени abs_time.
- Note:
алгоритм::suspend_until()
virtual void notify() noexcept = 0;
PROPS& properties( context * f) noexcept;
virtual void property_change( context * f, PROPS & properties) noexcept;
- Effects:
Сообщите пользовательскому планировщику о возможно значимом изменении свойства, принадлежащего волокну f. свойства содержат новые значения всех соответствующих свойств.
- Throws:
Ничего.
- Note:
Этот метод называется только тогда, когда пользовательский подкласс fiber_properties явно вызывает fiber_properties::notify().
virtual fiber_properties * new_properties( context * f);
- Returns:
Новый экземпляр fiber_properties подкласса PROPS.
- Note:
По умолчанию algorithm_with_properties<>::new_properties() просто возвращает newPROPS(f), помещая экземпляр PROPS на кучу. Переопределить этот способ для выделения PROPS каким-либо другим способом. Возвращенный указатель fiber_properties должен указывать на экземпляр PROPS, связанный с волокном f.
Хотя вы можете рассматривать контекст* как непрозрачный токен, некоторые члены контекст могут быть полезны для реализации пользовательского планировщика.
Особо следует отметить тот факт, что контекст содержит крючок для участия в буст::интрузивный::список<-- [ORIG_BEGIN]typedef’ed as boost::fibers::scheduler::ready_queue_t.
This hook is reserved for use by algorithm implementations. (For
instance, round_robin contains a ready_queue_t
instance to manage its ready fibers.) See context::ready_is_linked(),
context::ready_link(), context::ready_unlink().
[ORIG_END] -->
Ваша реализация алгоритм может использовать любой контейнер, которым вы хотите управлять, прошедшие экземпляры контекст. ready_queue_t позволяет избежать некоторых накладных расходов типичных контейнеров STL.
#include <boost/fiber/context.hpp>
namespace boost {
namespace fibers {
enum class type {
none = unspecified,
main_context = unspecified,
dispatcher_context = unspecified,
worker_context = unspecified,
pinned_context = unspecified
};
class context {
public:
class id;
static context * active() noexcept;
context( context const&) = delete;
context & operator=( context const&) = delete;
id get_id() const noexcept;
void detach() noexcept;
void attach( context *) noexcept;
bool is_context( type) const noexcept;
bool is_terminated() const noexcept;
bool ready_is_linked() const noexcept;
bool remote_ready_is_linked() const noexcept;
bool wait_is_linked() const noexcept;
template< typename List >
void ready_link( List &) noexcept;
template< typename List >
void remote_ready_link( List &) noexcept;
template< typename List >
void wait_link( List &) noexcept;
void ready_unlink() noexcept;
void remote_ready_unlink() noexcept;
void wait_unlink() noexcept;
void suspend() noexcept;
void set_ready( context *) noexcept;
};
bool operator<( context const& l, context const& r) noexcept;
}}
static context * active() noexcept;
- Returns:
Укажите на пример текущего волокна.
- Throws:
Ничего
context::id get_id() const noexcept;
- Returns:
Если * это относится к волокну исполнения, экземпляр волокно::id, который представляет это волокно. В противном случае возвращается построенный по умолчанию fiber::id.
- Throws:
Ничего
- See also:
fiber::get_id()
void attach( context * f) noexcept;
- Precondition:
this->get_scheduler() ==nullptr
- Effects:
Прикрепите волокно f к планировщику, работающему *это.
- Postcondition:
this->get_scheduler() != nullptr
- Throws:
Ничего
- Note:
Типичный вызов: boost::::context::active->attach(f);
- Note:
f не должен быть контекстом работающего волокна’s. Он не должен быть заблокирован или прекращен. Он не должен быть pinned_context. В настоящее время он должен быть отделен. В настоящее время он не должен быть привязан к алгоритму реализации’s готовой очереди. Большинство из этих условий подразумевается, что f принадлежит реализации алгоритм, то есть он был передан алгоритм::пробужден(), но еще не был возвращен алгоритм::pick_next(). Как правило, реализация pick_next() будет называть attach() с контекстом*. Сначала необходимо удалить f из готовой очереди. Вы никогда не должны передавать pinned_context attach(), потому что вы никогда не должны были называть его detach().
void detach() noexcept;
- Precondition:
(this->get_scheduler()nullptr) && ! pinned_context
- Effects:
Отсоедините волокно *this от его планировщика, работающего *this.
- Throws:
Ничего
- Postcondition:
this->get_scheduler() ==nullptr
- Note:
Этот метод должен быть вызван нитью, с которой в настоящее время связано волокно. *Это не должно быть контекстом работающего волокна’s. Он не должен быть заблокирован или прекращен. Не должно быть pinned_context. Его уже нельзя отделять. Он не должен быть уже связан с алгоритмом реализации’s готовая очередь. Большинство из этих условий подразумевается *, что передается алгоритм::awakened(); реализация awakened() должна, однако, проверяться на pinned_context. Он должен вызывать detach()до, связывая * это в готовую очередь.
- Note:
В частности, ошибочно пытаться мигрировать волокно из одной нити в другую, называя оба метода detach() и attach() в методе algorithm::pick_next(). pick_next() вызывается в потоке предполагаемого назначения. detach() должен быть вызван исходным потоком волокна’s. Вы должны позвонить detach() в соответствующем методе awakened().
- Note:
Если вы не намерены сделать волокно доступным для потенциальной миграции в другую нить, вы не должны называть ни detach(), ни attach() с его контекстом.
bool is_context( type t) const noexcept;
- Returns:
true, если * это имеет указанный тип.
- Throws:
Ничего
- Note:
type::worker_context здесь означает любое волокно, не специальное для библиотеки. Для type::main_context context связан с “main”волокном резьбы: неявно созданным самим потоком Boost.Fiber.Для type::dispatcher_context context связан с “dispatching”волокном, ответственным за отправку пробудившихся волокон в готовую очередь планировщика’. Оптоволокно “dispatching” является деталью реализации оптоволоконного менеджера. Контекст “main” или “dispatching” fiber — любое волокно, для которого is_context(pinned_context) является true — никогда не должно передаваться context::detach().
bool is_terminated() const noexcept;
- Returns:
true, если * этот больше не является допустимым контекстом.
- Throws:
Ничего
- Note:
Контекст вернулся из своей оптоволоконной функции и больше не считается допустимым контекстом.
bool ready_is_linked() const noexcept;
- Returns:
true, если * это хранится в алгоритме реализации’s готовой очереди.
- Throws:
Ничего
- Note:
В частности, этот метод указывает, был ли контекст::ready_link() вызван на *это. ready_is_linked() не имеет информации об участии в каких-либо других контейнерах.
bool remote_ready_is_linked() const noexcept;
- Returns:
true, если * это хранится в диспетчере волокон’s удаленной очереди.
- Throws:
Ничего
- Note:
контекст, сигнализированный как готовый другим потоком, сначала хранится в диспетчере волокон & #8217;s удаленной очереди. Это механизм, с помощью которого диспетчер волокон защищает реализацию алгоритма от поперечных вызовов алгоритма::awakened().
bool wait_is_linked() const noexcept;
- Returns:
true, если * это хранится в очереди ожидания некоторого объекта синхронизации.
- Throws:
Ничего
- Note:
контекст волокна, ожидающего на объекте синхронизации (например, mutex, condition_variable и т.д.) хранится в очереди ожидания этого объекта синхронизации.
template< typename List >
void ready_link( List & lst) noexcept;
- Effects:
Хранит *это в готовой очереди lst.
- Throws:
Ничего
- Note:
Аргумент lst должен представлять собой двойной список из Boost.Intrusive, например, экземпляр boost::fibers::scheduler::ready_queue_t. В частности, он должен быть boost::intrusive::list совместим с list_member_hook, хранящимся в объекте context.
template< typename List >
void remote_ready_link( List & lst) noexcept;
- Effects:
Хранит *это в готовой очереди lst.
- Throws:
Ничего
- Note:
Аргумент lst должен представлять собой двойной список из Boost.Intrusive.
template< typename List >
void wait_link( List & lst) noexcept;
- Effects:
Хранит *это в готовой очереди lst.
- Throws:
Ничего
- Note:
Аргумент lst должен представлять собой двойной список из Boost.Intrusive.
void ready_unlink() noexcept;
void remote_ready_unlink() noexcept;
- Effects:
Удалите *this из удаленной очереди.
- Throws:
Ничего
void wait_unlink() noexcept;
- Effects:
Удалить *это из очереди ожидания.
- Throws:
Ничего
void suspend() noexcept;
- Effects:
Приостанавливает работающее волокно (волокно, связанное с *this) до тех пор, пока некоторое другое волокно не пройдет this до контекст::set_ready(). *this помечается как неготовое, и управление переходит к планировщику, чтобы выбрать другое волокно для запуска.
- Throws:
Ничего
- Note:
Это низкоуровневый API, потенциально полезный для интеграции с другими фреймворками. Он не предназначен для непосредственного использования типичной прикладной программой.
- Note:
Обязанностью абонента является организация вызова set_ready() с указателем на this в некоторое время в будущем.
void set_ready( context * ctx ) noexcept;
- Effects:
Отметьте волокно, связанное с контекстом *ctx, как готовое к работе. Это не сразу возобновляет это волокно; скорее оно передает волокно планировщику для последующего возобновления. Если планировщик простаивает (не вернулся с вызова на алгоритм::suspend_until()), алгоритм::notify() призван разбудить его.
- Throws:
Ничего
- Note:
Это низкоуровневый API, потенциально полезный для интеграции с другими фреймворками. Он не предназначен для непосредственного использования типичной прикладной программой.
- Note:
Явно поддерживается вызов set_ready(ctx) из потока, отличного от того, на котором в настоящее время приостановлен *ctx. Соответствующее волокно будет возобновлено на исходной нити в надлежащее время.
bool operator<( context const& l, context const& r) noexcept;
- Returns:
true, если l.get_id< r.get_id() является true, false в противном случае.
- Throws:
Ничего.