17

メインスレッドで関数を実行する方法はありますか?

したがって、ファイルをダウンロードしてからデータを解析する Async を介して関数を呼び出すとします。次に、メイン UI スレッドで実行され、UI を更新するコールバック関数を呼び出しますか?

デフォルトの C++ 実装ではスレッドが同等であることはわかっているので、メイン スレッドへの共有ポインタを作成する必要があります。これをどのように行い、非同期関数をメインスレッドへの共有ポインターだけでなく、rrun したい関数へのポインターも渡し、そのメインスレッドで実行しますか?

4

2 に答える 2

17

私は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));
    }
}

ノート:

  1. 私は (常に) 学習しているので、私のコードが良くない可能性は十分にあります。ただし、コンセプトは少なくとも健全です。

  2. std::async(a )からの戻り値のデストラクタは、操作が完了しstd::future<>て開始されるまでブロックされるため (「 」を参照)、タスクの結果を (私の例で行っているように) で待機することは、素晴らしいアイデアではないかもしれません。std::asyncstd::asyncone_off

  3. コードの読みやすさ/保守性/何とか何とかを改善するために、独自のスレッドセーフな MessageQueue タイプを作成することをお勧めします (少なくとも私はそうします)。

  4. 指摘したいことがもう 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
于 2013-06-28T01:47:04.387 に答える