This page goes over the absolute basics of Boost fibers [1].
It covers creating and joining fibers.
Readers familiar with fibers might find starting with the producer-consumer page covering fibers more useful.
The page will start with simple examples showing how fibers are created and joined and will look at the way fiber scheduling affects the order of execution.
Additional code is used when including the fiber library due to an issue when using the Boost library. An explanation and the solution can be found in [2].
A custom class containing all the example functions will be used.
This usage of a class is done because the creation of fibers works differently for static and non-static thread functions.
When the function the fiber should run is not part of any class, the same code as the example for static functions can be used.
The class is defined as follows in the header file:
When a fiber is created, it will have the type boost::fibers::fiber.
A fiber can be created using boost:fibers::fiber(functionName), creating a fiber that executes the function named functionName.
This fiber can be stored in a variable with the type boost::fibers::fiber.
Fibers can also be saved using the assignment operator or a shorthand notation.
The scheduler used by threads will handle calling the fiber.
The programmer does need to join the fiber.
Joining the fiber ensures it will finish before going out of scope by pausing the main function until this has happened.
The following code example creates a fiber and joins it to ensure it finishes running.
When using non-static functions in a class, the class instance of the function also needs to be provided.
This instance can be provided using the second parameter of the fiber constructor.
If the function creating the fiber belongs to the same class instance as the fiber, then the ‘this’ keyword can reference the class instance.
Any required parameters can be added after the function name and optional instance parameters.
Unlike threads, which use preemptive scheduling, fibers use cooperative scheduling.
This use of cooperative scheduling means that fibers must define when they yield, which is not required when using threads.
This yield is done using the boost::this_fiber::yield() function.
Adding this function in the function the fiber will run will make the fiber yield when it executes that line of code.
When the fiber is called again, it starts right after the line where it was yielded.
The following example tries to illustrate this behaviour.
Three fibers will be created, and each will print its number twice.
A version with and without the yield statement will illustrate the difference. The version that uses yield has been edited explicitly, so the first fiber does not use the yield function because this illustrates the usage of the scheduler better than when all functions yield.
The default scheduler of Boost fibers uses the round-robin method to pick its next fiber [3].
When choosing fibers, a first in, first out queue is used.
The main function can also be seen as part of this queue.
In the noYield example, fiber1.join() will be called, causing the main function to stop and go to the back of the queue. Because all fibers finish in a single call, they all print their number twice, after which the main function takes over again and goes over the subsequent joins, which do nothing anymore because the fibers they join have already been completed.
In the yield example, the main fiber will wait for fiber1 to join and stop.
Fiber 1 takes over and completes in a single call, after which fiber2 and fiber3 are next in the queue. After all three fibers have been called, the main function will resume because fiber1 has finished. The main function then joins fiber2, and fiber2 and fiber3 are completed, after which the main function finishes.
The yield example shows that the order in which fibers are created matters because this is the order in which they are inserted into the queue.
Although unimportant in this small example, this creation order can impact the performance of a program, as will be explained in the context of the producer-consumer example on the next page.
[2] Zitrax and H. Passant. “Answer to warning C4003: not enough actual parameters for macro ‘max’ - Visual Studio 2010 C++.” stackoverflow.com. Accessed: Mar. 1, 2024. [Online.] Available: https://stackoverflow.com/a/6884251