How-To use tasklets
So, threads are single-use things, right? You create them, start them, stop them, and then you need to create a new one.
It makes sense, of course, but it can be a little awkward. You may want to have something that runs for a while, you stop it, but then you want to restart it.
Using the standard library thread interface, you need recreate a thread object here. If you want the thread to sleep and wake up, you need to pass it some kind of condition, and a flag whether it’s still alive. And to access the condition, you need some mutex or other.
It’s not complicated, but there’s a bunch of boilerplate for simply running
something in the background. Sometimes it’s nice to have something a little
more durable. Like a task, but lighter weight. How about calling it a
tasklet
?
1#include <liberate/concurrency/tasklet.h>
2
3using namespace liberate::concurrency;
4
5void my_task(tasklet::context & ctx)
6{
7 while (ctx.running) {
8 // Do your thing, then sleep
9 ctx.sleep(std::chrono::milliseconds{20});
10 }
11}
You implement a tasklet in a simple function that accepts a tasklet
liberate::concurrency::tasklet::context
; this context
contains:
An atomic boolean flag whether the tasklet is still running.
A sleep function for putting the tasklet to sleep. This is interruptible, of course.
Note that the function can, of course, be bound to some object’s member function. We’re just keeping it simple for here and now.
With that, we can create a restartable liberate::concurrency::tasklet
:
1tasklet t{my_task};
2
3t.start(); // does stuff
4t.stop(); // stops doing stuff
5t.wait(); // wait for thread to finish
6t.start(); // starts again
7t.wakeup(); // wake task up if it sleeps
That’s pretty much all there is. It should be clear from context that once you
call start()
, the running
flag is set; when you call stop()
it is
unset and the sleep is interrupted.
There’s one more thing. Suppose you have several threads and you want them to
wait on the same condition? That is supported. Instead of just creating the
tasklet, you can pass it a liberate::concurrency::tasklet::sleep_condition
pointer.
1tasklet::sleep_condition cond;
2
3tasklet t1{my_task, &cond, true};
4tasklet t2{my_task, &cond, true};
Note the third argument passed to the tasklet
constructor – it immediately
asks the tasklet to start()
upon creation.
And that’s it! You now have two threads waiting on the same condition.