Game Development Reference
In-Depth Information
if (GetPlayerWon())
{
SerializationManager::GetSingleton().ClearSave();
cout << "Congratulations, you rid the dungeon of monsters!" << endl;
cout << "Type goodbye to end" << endl;
std::string input;
cin >>input;
}
}
The first step was to create a function,
LoadSaveGame
, to be executed in another thread.
LoadSaveGame
calls the
SerializationManager::Load
method. The
LoadSaveGame
function pointer is
passed into the
packaged_task
constructor. The
packaged_task
template has been specialized with
the type
bool()
. This is the type of the function; it returns a
bool
and does not take any parameters.
Then
std::ref
is used to pass the
packaged_task
into a thread. When a
packaged_task
is passed to
a thread it can be executed, as a thread object knows how to handle
packaged_task
objects. This
is true because a
packaged_task
object overloads an operator, which allows it to be called just like
a function. This overloaded function call operator calls the actual function used to construct the
packaged_task
.
The main thread can now call the
get_future method
on the
packaged_task
. A
future
is used in
threaded programs to allow you to set up tasks that will provide returned values at some point in
the
future
. You could call
get
immediately on the
future,
but as
get
is a blocking call, your thread
would stall until the
future
result is available. Listing 25-9 shows an alternate implementation where
wait_for
is used to check if the
future
result is available.
The
future::wait_for
method takes a value from the
std::chrono
set of duration classes. In this
case, we are passing in
std::chrono::seconds{ 0 }
, which means the method will return instantly
with a result. The possible return values in our case come from the
std::future_status enum class
and are
ready
or
timeout
. The
timeout
value will be returned until the player's game is loaded or he
or she chooses to start a new game. At that point we can call the
future::get
method, which stores
the value returned from
SerializationManager::Loa
d, via the
LoadSaveGame
function passed to
loaderTask
.
That wraps up your brief introduction to multithreaded C++ programming.
Summary
In this chapter you have been introduced to some of the classes C++ provides to allow you to add
multiple execution threads to your programs. You first saw how threads can be created to execute
functions. Calling functions in this manner allows the operating system to run your threads on more
than one CPU thread and speed up the execution of your program.
When you use threads you need to make sure that your threads do not conflict when accessing
variables and sharing data. You saw that the mutex can be used to manually provide mutually exclusive
access to variables. After showing a
mutex
in action, I then introduced you to the
packaged_task
template, which automatically creates a promise and a future to better manage your concurrent
tasks at a higher level than the base thread and mutex.