私はC++ Concurrency in Actionを読んでいて、第 4 章 (AKA "The Chapter I Just Finished") で解決策を説明しています。
ショートバージョン
共有std::deque<std::packaged_task<void()>>
(または同様の種類のメッセージ/タスク キュー) を用意します。起動std::async
された関数はタスクをキューにプッシュでき、GUI スレッドはループ中にタスクを処理できます。
実際には長いバージョンはありませんが、例を次に示します
共有データ
std::deque<std::packaged_task<void()>> tasks;
std::mutex tasks_mutex;
std::atomic<bool> gui_running;
std::async
機能_
void one_off()
{
std::packaged_task<void()> task(FUNCTION TO RUN ON GUI THREAD); //!!
std::future<void> result = task.get_future();
{
std::lock_guard<std::mutex> lock(tasks_mutex);
tasks.push_back(std::move(task));
}
// wait on result
result.get();
}
GUI スレッド
void gui_thread()
{
while (gui_running) {
// process messages
{
std::unique_lock<std::mutex> lock(tasks_mutex);
while (!tasks.empty()) {
auto task(std::move(tasks.front()));
tasks.pop_front();
// unlock during the task
lock.unlock();
task();
lock.lock();
}
}
// "do gui work"
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
ノート:
私は (常に) 学習しているので、私のコードが良くない可能性は十分にあります。ただし、コンセプトは少なくとも健全です。
std::async
(a )からの戻り値のデストラクタは、操作が完了しstd::future<>
て開始されるまでブロックされるため (「 」を参照)、タスクの結果を (私の例で行っているように) で待機することは、素晴らしいアイデアではないかもしれません。std::async
std::async
one_off
コードの読みやすさ/保守性/何とか何とかを改善するために、独自のスレッドセーフな MessageQueue タイプを作成することをお勧めします (少なくとも私はそうします)。
指摘したいことがもう 1 つありますが、今は忘れてしまいます。
完全な例
#include <atomic>
#include <chrono>
#include <deque>
#include <iostream>
#include <mutex>
#include <future>
#include <thread>
// shared stuff:
std::deque<std::packaged_task<void()>> tasks;
std::mutex tasks_mutex;
std::atomic<bool> gui_running;
void message()
{
std::cout << std::this_thread::get_id() << std::endl;
}
void one_off()
{
std::packaged_task<void()> task(message);
std::future<void> result = task.get_future();
{
std::lock_guard<std::mutex> lock(tasks_mutex);
tasks.push_back(std::move(task));
}
// wait on result
result.get();
}
void gui_thread()
{
std::cout << "gui thread: "; message();
while (gui_running) {
// process messages
{
std::unique_lock<std::mutex> lock(tasks_mutex);
while (!tasks.empty()) {
auto task(std::move(tasks.front()));
tasks.pop_front();
// unlock during the task
lock.unlock();
task();
lock.lock();
}
}
// "do gui work"
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main()
{
gui_running = true;
std::cout << "main thread: "; message();
std::thread gt(gui_thread);
for (unsigned i = 0; i < 5; ++i) {
// note:
// these will be launched sequentially because result's
// destructor will block until one_off completes
auto result = std::async(std::launch::async, one_off);
// maybe do something with result if it is not void
}
// the for loop will not complete until all the tasks have been
// processed by gui_thread
// ...
// cleanup
gui_running = false;
gt.join();
}
データ出力
$ ./messages
main thread: 140299226687296
gui thread: 140299210073856
140299210073856
140299210073856
140299210073856
140299210073856
140299210073856