Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++11

A Simple Worker Thread

5.00/5 (5 votes)
15 Mar 2016CPOL1 min read 40.1K   949  
A worker thread gives you parallel processing power

Introduction

Sometimes, we need an extra thread to do a task for us in parallel or to just unblock main thread. For better performance, we would not like to create a new thread every time. Also, we need little more control over the thread. In this article, we will implement a thread with the below abilities.

  • You need not define a thread procedure, rather you can submit any function using lambda
  • Implemented purely in C++11, compatible with any OS
  • Submit a task to be executed asynchronously
  • Submit a task to be executed synchronously (but on worker thread)
  • Automatically wait for task to complete
  • Implementation uses C++ 11 extensively (thread, lambda, condition variables)

Using the Code

The below sample code lists usages of worker thread:

C++
#include <iostream>
#include <chrono>
#include <thread>
#include "workerthread.h"

int main()
{
    std::cout << "Hi, Welcome to demo of worker thread" << std::endl;

    {
        // Create two worker threads
        WorkerThread thread;
        WorkerThread thread2;

        // Example of a synchronous task
        thread.doSync([]{    std::cout << "First - blocking call" << std::endl;    });

        for (int i = 1; i < 100; i++)
        {
            auto& t = i % 2 == 0 ? thread : thread2;
            if (i == 10) // Another synchronous task in between
            {
                thread.doSync([]{    
                std::cout << "Second - blocking call" << std::endl;    });
            }
            // Multiple asynchronous tasks queued
            t.doAsync([i]
            {
                std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(200));
                std::cout << (i % 2 == 0 ? "thread-" : "thread2-") 
                << "iteration number: " << i << std::endl;
            });
        }
    
        thread.doSync([]{    std::cout << "Last - blocking call";    });
    }// Added extra scope to demo destruction of worker thread
    std::cout << "This must be last line\n";

}

Implementation of worker thread:

  • Use C++ function construct to store submitted task
  • Finish the present queue and wait for signal for new work
C++
void WorkerThread::startThread()
{
    std::unique_lock<std::mutex> l(mutex);
    do
    {
        while (isRunning && tasks.empty())
            itemInQueue.wait(l);

        while (!tasks.empty())
        {
            auto size = tasks.size();
            printf("Number of pending task are %d\n", size);
            const std::function<void()> t = tasks.front();
            tasks.pop_front();
            l.unlock();
            t();
            l.lock();
        }
        itemInQueue.notify_all();

    } while (isRunning);
    itemInQueue.notify_all();
}

An asynchronous task is just queued and calling thread not blocked:

C++
void WorkerThread::doAsync(const std::function<void()>& t)
{
    std::lock_guard<std::mutex> _(mutex);
    tasks.push_back(t);
    itemInQueue.notify_one();
}

Synchronous task is little trickier, we need to block calling thread until all tasks queued before the submitted task and submitted task completes.

C++
void WorkerThread::doSync(const std::function<void()>& t)
{
    std::condition_variable event;
    bool finished = false;

    std::unique_lock<std::mutex> l(mutex);
    auto lambda = [this, &t, &finished, &event]
    {
        t();
        std::lock_guard<std::mutex> l(mutex);

        finished = true;
        event.notify_one();
    };
    tasks.push_back(lambda);
    itemInQueue.notify_one();

    while (!finished)
        event.wait(l);
}

With minor changes, this class can be used to take priority base task.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)