Synchronizing very fast threads

Borislav Stanimirov

In the following example (an idealized "game") there are two threads. The main thread which updates data and RenderThread which "renders" it to the screen. What I need it those two to be synchronized. I cannot afford to run several update iteration without running a render for every single one of them.

I use a condition_variable to sync those two, so ideally the faster thread will spend some time waiting for the slower. However condition variables don't seem to do the job if one of the threads completes an iteration for a very small amount of time. It seems to quickly reacquire the lock of the mutex before wait in the other thread is able to acquire it. Even though notify_one is called

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <functional>
#include <mutex>
#include <condition_variable>

using namespace std;

bool isMultiThreaded = true;

struct RenderThread
{
    RenderThread()
    {
        end = false;
        drawing = false;
        readyToDraw = false;
    }

    void Run()
    {
        while (!end)
        {
            DoJob();
        }
    }

    void DoJob()
    {
        unique_lock<mutex> lk(renderReadyMutex);
        renderReady.wait(lk, [this](){ return readyToDraw; });
        drawing = true;

        // RENDER DATA
        this_thread::sleep_for(chrono::milliseconds(15)); // simulated render time
        cout << "frame " << count << ": " << frame << endl;
        ++count;

        drawing = false;
        readyToDraw = false;

        lk.unlock();
        renderReady.notify_one();
    }

    atomic<bool> end;

    mutex renderReadyMutex;
    condition_variable renderReady;
    //mutex frame_mutex;
    int frame = -10;
    int count = 0;

    bool readyToDraw;
    bool drawing;
};

struct UpdateThread
{
    UpdateThread(RenderThread& rt)
        : m_rt(rt)
    {}

    void Run()
    {
        this_thread::sleep_for(chrono::milliseconds(500));

        for (int i = 0; i < 20; ++i)
        {
            // DO GAME UPDATE

            // when this is uncommented everything is fine
            // this_thread::sleep_for(chrono::milliseconds(10)); // simulated update time

            // PREPARE RENDER THREAD
            unique_lock<mutex> lk(m_rt.renderReadyMutex);
            m_rt.renderReady.wait(lk, [this](){ return !m_rt.drawing; });

            m_rt.readyToDraw = true;

            // SUPPLY RENDER THREAD WITH DATA TO RENDER
            m_rt.frame = i;

            lk.unlock();
            m_rt.renderReady.notify_one();

            if (!isMultiThreaded)
                m_rt.DoJob();
        }        

        m_rt.end = true;
    }

    RenderThread& m_rt;
};

int main()
{
    auto start = chrono::high_resolution_clock::now();

    RenderThread rt;
    UpdateThread u(rt);

    thread* rendering = nullptr;
    if (isMultiThreaded)
        rendering = new thread(bind(&RenderThread::Run, &rt));

    u.Run();

    if (rendering)
        rendering->join();

    auto duration = chrono::high_resolution_clock::now() - start;
    cout << "Duration: " << double(chrono::duration_cast<chrono::microseconds>(duration).count())/1000 << endl;


    return 0;
}

Here is the source of this small example code, and as you can see even on ideone's run the output is frame 0: 19 (this means that the render thread has completed a single iteration, while the update thread has completed all 20 of its).

If we uncomment line 75 (ie simulate some time for the update loop) everything runs fine. Every update iteration has an associated render iteration.

Is there a way to really truly sync those threads, even if one of them completes an iteration in mere nanoseconds, but also without having a performance penalty if they both take some reasonable amount of milliseconds to complete?

Adrian Maire

If I understand correctly, you want the 2 threads to work alternately: updater wait until the renderer finish before to iterate again, and the renderer wait until the updater finish before to iterate again. Part of the computation could be parallel, but the number of iteration shall be similar between both.

You need 2 locks:

  • one for the updating
  • one for the rendering

Updater:

wait (renderingLk)
update
signal(updaterLk)

Renderer:

wait (updaterLk)
render
signal(renderingLk)

EDITED:

Even if it look simple, there are several problems to solve:

Allowing part of the calculations to be made in parallel: As in the above snippet, update and render will not be parallel but sequential, so there is no benefit to have multi-thread. To a real solution, some the calculation should be made before the wait, and only the copy of the new values need to be between the wait and the signal. Same for rendering: all the render need to be made after the signal, and only getting the value between the wait and the signal.

The implementation need to care also about the initial state: so no rendering is performed before the first update.

The termination of both thread: so no one will stay locked or loop infinitely after the other terminate.

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

Synchronizing var instantiation upon multiple threads

分類Dev

How to undo in turtle very fast?

分類Dev

NSIS' uninstaller works very fast

分類Dev

TPL Dataflow - very fast producer, not so fast consumers OutOfMemory exception

分類Dev

fast way of populating a very large dataframe with values

分類Dev

SftpInboundFileSynchronizer not synchronizing

分類Dev

I have seemingly fast ping times on my wireless network, but sometimes when I try to access fast websites, resolving host is very slow

分類Dev

Synchronizing webapps across servers

分類Dev

Synchronizing a for loop with timers in Swift

分類Dev

Synchronizing pthreads using mutex in C

分類Dev

Synchronizing a one-to-many relationship in Laravel

分類Dev

Synchronizing two render passes: color attachment to sampled?

分類Dev

Strange Results Synchronizing Player Positions over Network

分類Dev

Synchronizing files using 7-Zip and the CLI

分類Dev

Synchronizing sync and local chrome.storage

分類Dev

Why the new google places api is not synchronizing with build.gradle?

分類Dev

Iterate a multi-threaded list without synchronizing the entire process

分類Dev

Synchronizing subscribers to JMS Topic in active/active mode (WebLogic)

分類Dev

Threads not communicating

分類Dev

kernel threads and user threads priorities

分類Dev

google apps script very very slow

分類Dev

Very easy iptables tutorial?

分類Dev

Very strange tensorflow behavior

分類Dev

Implementing a very basic IDisposable

分類Dev

Very slow parsing in AVD

分類Dev

Resolution to a very slow network

分類Dev

'ls' command very slow

分類Dev

Very Simple usage of ngModel

分類Dev

swift fast enumeration of optionals

Related 関連記事

ホットタグ

アーカイブ