-
Notifications
You must be signed in to change notification settings - Fork 1
03. Threads
Implementing threads in C++ prior to C++11 was fairly tedious and platform specific. You could make these much simpler using Boost threads but since C++11 we have built-in threading.
It is still however a good idea to wrap your threads in classes to nicely manage them and to save having to repeat yourself when writing threaded code.
As such this part of CoreLibrary has several classes related to threading that aim to make life a bit easier when implementing threading.
I’ll show some examples of the threading utility classes providing threaded base class, message queue thread, event triggered thread, as well as thread group management, thread-safe queue and a bounded buffer.
These examples are in no way exhaustive in demonstrating all you can do with the code in the Threads sub-library within CoreLibrary but they should give an idea of general usage.
Look in the unit tests for more examples.
Creating a group of threads from functions.
#include <iostream> // For std::cout.
#include <mutex> // For std::mutex and std::lock_guard.
#include <chrono> // To support std::this_thread::sleep_for.
#include "ThreadGroup.h" // For core_lib::threads::ThreadGroup.
// Make sure we can use ThreadGroup.
using core_lib::threads::ThreadGroup;
// A mutex to protect access to std::cout.
std::mutex g_coutMutex;
void ThreadFunc()
{
// Grab our thread ID.
auto id = std::this_thread::get_id();
// Do some work for 100 iterations, after which this thread
// will terminate.
for (int i = 1; i <= 100; ++i)
{
{
// Protect access to std::cout as we'll be accessing
// it from multiple threads.
std::lock_guard<std::mutex> lock(g_coutMutex);
std::cout << "Thread ID: " << id << ", count: " << i << std:endl;
}
// Wait for a short period of time.
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
unsigned int GetNumThreads()
{
// Create n threads each running the same function.
auto n = std::thread::hardware_concurrency()
if (0 == n)
{
n = 8;
}
return n;
}
int main()
{
// Create thread group.
ThreadGroup tg;
// Create n threads each running the same function.
auto n = GetNumThreads();
for (unsigned int i = 0; i < n; ++i)
{
auto id = tg.CreateThread(ThreadFunc)->get_id();
// Protect access to std::cout as we'll be accessing
// it from multiple threads.
std::lock_guard<std::mutex> lock(g_coutMutex);
std::cout << "Thread created with ID: " << id << std:endl;
}
// Wait for the 8 threads to finish.
tg.JoinAll();
return 0;
}
Adding std::thread objects to a thread group.
// This example is the same as Example 1 except for the main function.
#include <memory> // For std::make_unique.
int main()
{
// Create thread group.
ThreadGroup tg;
// Create n threads each running the same function.
auto n = GetNumThreads();
for (unsigned int i = 0; i < n; ++i)
{
auto t = std::make_unique<std::thread>(ThreadFunc);
{
// Protect access to std::cout as we'll be accessing
// it from multiple threads.
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "Thread created with ID: " << t->get_id() << std:endl;
}
tg.AddThread(t.release());
}
// Wait for the 8 threads to finish.
tg.JoinAll();
return 0;
}
Synchronising 2 threads with SyncEvent where one thread waits for another thread to signal.
#include <iostream> // For std::cout.
#include <mutex> // For std::mutex and std::lock_guard.
#include <thread> // For std::thread.
#include <chrono> // To support std::this_thread::sleep_for.
#include "SyncEvent.h" // For core_lib::threads::SyncEvent.
// Make sure we can use SyncEvent.
using core_lib::threads::SyncEvent;
// A mutex to protect access to std::cout.
std::mutex g_mutex;
// A variable to count iterations.
int g_count = 0;
void ThreadFunc(SyncEvent& event)
{
// Do some work for 10 iterations, after which this thread
// will terminate.
for (int i = 0; i < 10; ++i)
{
{
// Protect access to counter.
std::lock_guard<std::mutex> lock(g_mutex);
g_count = i + 1;
}
// Signal our event.
event.Signal();
// Wait for a short period of time.
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main()
{
// Create sync event.
SyncEvent event;
// Create a thread.
auto t = std::thread(ThreadFunc, event);
std::cout << "Thread created with ID: " << t->get_id() << std:
while (true)
{
// Wait for thread to signal.
event.Wait();
// Get counter value and print it.
int count;
{
std::lock_guard<std::mutex> lock(g_mutex);
count = g_count;
}
std::cout << "Thread signalled, iteration count: "
<< count << std::endl;
if (10 == count)
{
break;
}
}
// Make sure thread has really finished.
if (t.joinable())
{
t.join();
}
return 0;
}
More coming soon...
All the code included in this project is licensed under the terms of LGPL 3.0 and the relevant documentation for this can be found at the top of each source file and in the LICENSE text file.
The code is the work of me (Duncan Crutchley) (dac1976github@outlook.com).
Copyright (C) 2014 onwards, Duncan Crutchley.