C++ のマルチスレッド最適化手法

王林
リリース: 2023-08-22 12:53:12
オリジナル
1007 人が閲覧しました

C++ のマルチスレッド最適化手法

コンピューター技術の発展とハードウェアのパフォーマンスの向上により、マルチスレッド技術は現代のプログラミングに不可欠なスキルになりました。 C は、多くの強力なマルチスレッド テクノロジも提供する古典的なプログラミング言語です。この記事では、読者がマルチスレッド テクノロジをより適切に適用できるように、C でのマルチスレッド最適化手法をいくつか紹介します。

1. std::thread を使用する

C 11 では std::thread が導入され、マルチスレッド テクノロジが標準ライブラリに直接統合されました。 std::thread を使用して新しいスレッドを作成するのは非常に簡単で、関数ポインターを渡すだけです。例:

#include <thread>
#include <iostream>

void hello()
{
    std::cout << "Hello World!";
}

int main()
{
    std::thread t(hello);
    t.join();
    return 0;
}
ログイン後にコピー

上記のコードは、新しいスレッド t を作成し、hello 関数を実行して、スレッド t が完了するまで待機します。スレッドの作成と破棄にはある程度のオーバーヘッドが必要なため、std::thread を合理的に使用する必要があることに注意してください。

2. std::async を使用する

std::async も便利なマルチスレッド テクノロジであり、関数を非同期に実行して std::future オブジェクトを返すことができます。 std::async を使用すると、非同期タスクの実行をより簡単に管理し、結果を取得できます。例:

#include <future>
#include <iostream>

int add(int a, int b)
{
    return a + b;
}

int main()
{
    auto async_result = std::async(add, 1, 2);
    std::cout << async_result.get();
    return 0;
}
ログイン後にコピー

上記のコードは、add 関数を呼び出して 1 2 を非同期に計算し、std::future オブジェクトを使用して計算結果の取得を管理します。 std::async はデフォルトで std::launch::async 戦略を使用し、新しいスレッドで関数を実行することに注意してください。 std::launch::deferred 戦略を使用する場合は、手動で指定する必要があります。ただし、 std::launch::deferred 戦略を使用すると、関数は std::future::get() が呼び出されたときにのみ実行されるため、ケースバイケースで選択する必要があります。

3. std::condition_variable を使用する

マルチスレッド プログラミングでは、スレッド間で通信と同期を実行する必要があり、std::condition_variable はこの目的をうまく達成できます。 std::condition_variable を使用すると、あるスレッドが別のスレッドの特定の条件が true になるまで待機できるため、スレッド間の同期が実現されます。例:

#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>

std::mutex mutex;
std::condition_variable cv;
bool ready = false;

void producer()
{
    std::unique_lock<std::mutex> lock(mutex);
    // wait for the condition to become true
    cv.wait(lock, [] { return ready; });
    std::cout << "Producer done." << std::endl;
}

void consumer()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    ready = true;
    std::cout << "Consumer done." << std::endl;
    cv.notify_one();
}

int main()
{
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}
ログイン後にコピー

上記のコードは 2 つのスレッド t1 と t2 を作成します。このうち、t1 は条件変数が true になるのを待つ前に待機状態にあり、t2 は条件変数を true になるのを待った後に設定します。 1秒.trueとしてt1に通知します。複数のスレッドが同時に条件変数にアクセスすることを防ぐために、 std::condition_variable を std::mutex と組み合わせて使用​​する必要があることに注意してください。

4. スレッド プールを使用する

大量の短期タスクを作成して実行する必要がある場合、スレッド プールは、タスクのパフォーマンスを向上させるためによく使用されます。プログラム。スレッド プールは一定数のスレッドを維持し、タスクの割り当てと実行を管理します。スレッド プールを使用すると、マルチコア CPU を最大限に活用しながら、頻繁にスレッドを作成および破棄することによる追加のオーバーヘッドを回避できます。例:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <functional>

class ThreadPool
{
public:
    ThreadPool(std::size_t numThreads = std::thread::hardware_concurrency())
    {
        for (std::size_t i = 0; i < numThreads; ++i)
        {
            pool.emplace_back([this] {
                while (!stop)
                {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock{ mutex };
                        condition.wait(lock, [this] { return stop || !tasks.empty(); });
                        if (stop && tasks.empty()) return;
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    ~ThreadPool()
    {
        {
            std::unique_lock<std::mutex> lock{ mutex };
            stop = true;
        }
        condition.notify_all();
        for (auto& worker : pool)
        {
            worker.join();
        }
    }

    template <typename F, typename... Args>
    auto enqueue(F&& f, Args&&... args)
        -> std::future<typename std::result_of<F(Args...)>::type>
    {
        using return_type = typename std::result_of<F(Args...)>::type;
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...));
        std::future<return_type> future = task->get_future();
        {
            std::unique_lock<std::mutex> lock{ mutex };
            if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");
            tasks.emplace([task](){ (*task)(); });
        }
        condition.notify_one();
        return future;
    }

private:
    std::vector<std::thread> pool;
    std::queue<std::function<void()>> tasks;
    std::mutex mutex;
    std::condition_variable condition;
    bool stop = false;
};

void hello()
{
    std::cout << "Hello World!" << std::endl;
}

int add(int a, int b)
{
    return a + b;
}

int main()
{
    {
        ThreadPool pool;
        auto f1 = pool.enqueue(hello);
        auto f2 = pool.enqueue(add, 1, 2);
        std::cout << f2.get() << std::endl;
    }
    return 0;
}
ログイン後にコピー

上記のコードは、複数のスレッドとタスク キューを含む ThreadPool クラスを定義します。スレッド プールは、タスク キューからタスクを取得し、キューが空になるかスレッド プールが停止するまで実行し続けます。 ThreadPool::enqueue メソッドを使用してタスクをタスク キューに追加し、std::future オブジェクトを返してタスクの実行結果を管理します。

一般に、C は、開発者がマルチコア CPU のパフォーマンスを活用し、スレッドとタスクをより柔軟に管理できるようにするさまざまなマルチスレッド テクノロジを提供します。開発者はこれらの手法を適切に使用して、プログラムのパフォーマンスを最適化する必要があります。

以上がC++ のマルチスレッド最適化手法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!