just::thread
Pro C++ Concurrency LibraryJust::Thread
Pro provides a set of library facilities to make
writing error-free multithreaded code easier. By using these facilities instead
of, or in addition to, the C++ Standard Library multithreading facilities, you
can avoid common pitfalls, while writing simpler code.
Just::Thread
Pro provides a
framework for creating actors that run on separate threads, and communicate via message passing,
as well
as jss::synchronized_value
for synchronizing access to a single object
and jss::concurrent_map
,
a hash map that is safe for concurrent access from multiple threads.
It also includes support for the Concurrency TS
including atomic_shared_ptr
and continuations.
For more information, please see below for an overview, or see the full documentation.
Order your copy of just::thread
Pro today, and get started within minutes.
synchronized_value
class template for synchronizing access to a single objectjss::lock_guard
class template to allow acquiring multiple locks at once, like std::scoped_lock
.atomic_shared_ptr
and
atomic_weak_ptr
— see Anthony's earlier
blog
post on atomic_shared_ptr
future::then()
— schedule a task to run when a future becomes ready.when_any()
— create a future that is ready when any of a set of futures is
ready.when_all()
— create a future that is ready when all of a set of futures are
ready.Using Actors provides an easy way of structuring your application to use multiple threads without
worrying about the details of mutexes, condition variables and other low-level facilities. Each
jss::actor
runs its task (which can be
a function or callable object) on its own thread, and has an associated message queue.
Actors then send messages to each other either by calling jss::actor::send()
directly on an jss::actor
object, or on an jss::actor_ref
object. Messages can be of any copyable or
movable type, so you can define your messages in whatever way is most appropriate. The
jss::actor::self()
function
provides an easy way to pass around a reference to the current actor. You can use this for passing a
reference to the current actor as part of a message, in order for the receiving actor to send a
response.
Messages are received by
calling jss::actor::receive()
. Calling jss::actor::receive()
on its own will just receive and discard all messages (as no matches are specified) until
a jss::stop_actor
message is
received (in which case
a jss::stop_actor
exception is
thrown). In order to process the messages you need to chain a call to match
on
the jss::actor::receive()
call, specifying the type of message to match, and the function to call to handle it. This could
be a lambda:
jss::actor::receive().match<my_message>([](my_message){ std::cout<<"my message received"<<std::endl; });
This will receive and discard all messages until either a my_message
message is
received, in which case the lambda is run to print "my message received" on standard output, or
a jss::stop_actor
message is
received. Once a message has been
handled, jss::actor::receive()
returns to its caller. If you wish to handle more than one message, just put
the jss::actor::receive()
call
in a loop.
Messages are sent with
the send()
member function:
jss::actor a(actor_func); a.send(my_message());
You can also specify that you wish to process one of several types of message, whichever arrives
first. This can be done by chaining multiple match
calls:
jss::actor::receive() .match<first_message>([](first_message){ std::cout<<"first message type received"<<std::endl; }) .match<second_message>([](second_message){ std::cout<<"second message type received"<<std::endl; });
This
time, jss::actor::receive()
will discard all messages until it receives either a message of type first_message
or
one of type second_message
(or
a jss::stop_actor
message). Again, it returns as soon as one message has been processed, whichever type it is. Any
number of match
calls can be chained in this way, and
the jss::actor::receive()
call
will block until one of the specified messages has been received.
The jss::mpsc_fifo
class
template provides an unbounded FIFO queue. Multiple threads may safely add items to the queue
concurrently, provided only a single thread is removing items from the queue. This is used to
provide the underlying message queue for the actors.
The jss::concurrent_map
class template provides a hash-based map which is safe for concurrent access from multiple
threads, including adding and removing elements concurrently with lookups and iteration. The
interface is modelled on std::unordered_map
, with an
added insert_or_replace
member function for replacing a stored element with another.
The jss::synchronized_value
class template provides synchronization for all accesses to a single object.
It is a simple wrapper around an object of the specified type, and the interface is modelled on a smart pointer, with accesses done using the pointer dereference and indirection operators.
The free function jss::apply
can also be used to perform a function on the wrapped
value(s) of one or more objects. The function is called with the locks on all supplied objects held;
the locks are released when the function returns or throws an exception.
jss::synchronized_value<std::string> s; jss::synchronized_value<std::string> s2; void foo(){ *s="hello"; *s2=" world"; jss::apply([](std::string& s,std::string & s2){ s += s2; },s,s2); }
In this example, multiple calls to foo
can happen concurrently on separate threads,
and all accesses to s
and s2
are protected.
std::experimental::latch
,
std::experimental::barrier
and std::experimental::flex_barrier
are features of the Concurrency TS. std::experimental::latch
is a single-use countdown latch which allows you to wait
for a set of threads to signal the latch, whereas std::experimental::barrier
and std::experimental::flex_barrier
are reusable barriers that allow a set of threads
to rendezvous at a particular point and only proceed when they are all ready. These can be great for
synchronizing batch operations or initialization steps.