Волокна в нити координируются диспетчером волокон. Контроль торговли волокнами осуществляется совместно, а не упреждающе: волокно, работающее в настоящее время, сохраняет контроль до тех пор, пока не вызовет некоторую операцию, которая передает контроль менеджеру. Каждый раз, когда волокно приостанавливается (или дает выход), менеджер волокна консультируется с планировщиком, чтобы определить, какое волокно будет работать дальше.
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:
Ничего.