Trong ví dụ sau (một trò chơi "lý tưởng") có hai chủ đề. Các chủ đề chính mà cập nhật dữ liệu và RenderThread
mà "ám" nó vào màn hình. Tôi cần hai thứ đó để được đồng bộ. Tôi không thể đủ khả năng để chạy một số bản cập nhật mà không cần chạy một render cho mỗi một trong số họ.Đồng bộ hóa các chủ đề rất nhanh
Tôi sử dụng condition_variable
để đồng bộ hóa hai thứ đó, vì vậy, lý tưởng là chuỗi nhanh hơn sẽ dành chút thời gian chờ chậm hơn. Tuy nhiên các biến điều kiện dường như không thực hiện công việc nếu một trong các chủ đề hoàn thành một lần lặp trong một khoảng thời gian rất nhỏ. Có vẻ như để nhanh chóng phản ứng lại khóa của mutex trước wait
trong chủ đề khác có thể thu được nó. Mặc dù notify_one
được gọi
#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, và như bạn có thể thấy ngay cả trên ideone ấy chạy ra là frame 0: 19
(điều này có nghĩa là làm cho chủ đề đã hoàn thành một sự lặp lại duy nhất, trong khi thread cập nhật đã hoàn thành tất cả 20 của mình).
Nếu chúng tôi bỏ ghi chú dòng 75 (tức là mô phỏng một thời gian cho vòng lặp cập nhật) mọi thứ đều chạy tốt. Mỗi lần lặp lại cập nhật đều có kết xuất lặp lại. Có một cách để thực sự thực sự đồng bộ hóa các chủ đề đó, ngay cả khi một trong số chúng hoàn thành một lần lặp chỉ trong một nano giây, nhưng cũng không có một hình phạt hiệu suất nếu cả hai mất một số tiền hợp lý của mili giây để hoàn thành?
Điều này giống như một người tiêu dùng sản xuất truyền thống Hãy xem http://vichargrave.com/multithreaded-work-queue-in-c/ – CreativeMind