Fibers are similar to threads, but are more lightweight and cooperative rather than pre-emptive. That is, a fiber must explicitly yield to pass control to another fiber, either by calling the yield
function directly or through a function that yields for you (such as an I/O function). On the other hand, threads will be scheduled automatically by the operating system and may be interrupted at any time.
Let us execute the following steps:
new Fiber
. You do not have to create a special storage location for your state because fibers automatically save and restore their own stacks, so regular local variables will work.Fiber.yield
periodically inside your fiber to let other fibers run. The yield
function will return control to the function that called the fiber.main
function is responsible for scheduling the fibers. It should call the call
function on the fibers when it is ready to resume their execution.Take a look at the following code:
import core.thread; int count; void testFunction() { import std.stdio; count++; writeln("Fiber run count: ", count); Fiber.yield(); writeln("Fiber recalled after yielding!"); } void main() { auto fiber1 = new Fiber(&testFunction); auto fiber2 = new Fiber(&testFunction); // Call the fiber, this will return when it calls Fiber.yield fiber1.call(); fiber2.call(); // now it will resume execution from the last yield call fiber1.call(); fiber2.call(); }
Running the program will print the following:
Fiber run count: 1 Fiber run count: 2 Fiber recalled after yielding! Fiber recalled after yielding!
A Fiber
represents the execution context of a function that can be paused and resumed. It is created similar to a Thread
, by first importing core.thread
and then creating the Fiber
by passing the constructor a pointer to the function.
However, after creation, Fibers and Threads work very differently. Whereas a Thread is started and then executes concurrently with other threads, a Fiber is called, like a function, with the flow of execution transferring to it. Unlike just any function though, a fiber can call Fiber.yield()
, which returns the execution to the caller while remembering the exact state it had when it yielded. Next time the fiber is called, it picks up where it left off the last time.
Since fibers are cooperative when multitasking, they yield
rather than being interrupted by the operating system. There is no need for memory synchronization or shared data. In the example, we used a thread-local variable, count
, to represent a value across two separate fibers, and it was effortlessly updated by both. The downside to this model is that they do not utilize multiple CPU cores the way threads can, and thus do not help with parallelism. Nevertheless, fibers can sometimes outperform threads depending on the task. Asynchronous I/O is an example where fibers shine because they are not bound by the CPU.
Fiber
class can be found at http://dlang.org/phobos/core_thread.html#Fiber3.141.192.120