サーチ…


構文

  • 糸()
  • スレッド(スレッド&&その他)
  • 明示的なスレッド(関数&& func、Args && ... args)

パラメーター

パラメータ詳細
other other所有権を引き継ぎ、 otherはもうスレッドを所有していない
func 別のスレッドで呼び出す関数
args func引数

備考

いくつかのメモ:

  • 2つのstd::threadオブジェクトは、同じスレッドを表すことできません
  • std::threadオブジェクトはスレッドを表現していない状態(移動後、 join呼び出し後など)になることがあります。

スレッド操作

スレッドを開始すると、スレッドが終了するまで実行されます。

多くの場合、結果を使用したいので、スレッドが完了するのを待つ必要があります(おそらく、スレッドがすでに終了している可能性があります)。

int n;
std::thread thread{ calculateSomething, std::ref(n) };

//Doing some other stuff

//We need 'n' now!
//Wait for the thread to finish - if it is not already done
thread.join();

//Now 'n' has the result of the calculation done in the seperate thread
std::cout << n << '\n';

スレッドをdetach 、スレッドを自由に実行させることもできます。

std::thread thread{ doSomething };

//Detaching the thread, we don't need it anymore (for whatever reason)
thread.detach();

//The thread will terminate when it is done, or when the main thread returns

スレッドへの参照の受け渡し

std::threadはそれらをコピー/移動するので、参照(またはconst参照)をスレッドに直接渡すことはできません。代わりに、 std::reference_wrapper使用しstd::reference_wrapper

void foo(int& b)
{
    b = 10;
}

int a = 1;
std::thread thread{ foo, std::ref(a) }; //'a' is now really passed as reference

thread.join();
std::cout << a << '\n'; //Outputs 10

void bar(const ComplexObject& co)
{
    co.doCalculations();
}

ComplexObject object;
std::thread thread{ bar, std::cref(object) }; //'object' is passed as const&

thread.join();
std::cout << object.getResult() << '\n'; //Outputs the result

std :: threadの作成

C ++では、スレッドはstd :: threadクラスを使用して作成されます。スレッドとは別の実行フローです。他のタスクを同時に実行している間にヘルパーに1つのタスクを実行させることに似ています。スレッド内のすべてのコードが実行されると、 終了します 。スレッドを作成するときには、実行するものを渡す必要があります。あなたがスレッドに渡すことができるいくつかの事柄:

  • フリー関数
  • メンバー関数
  • Functorオブジェクト
  • ラムダ式

フリー関数の例 - 別のスレッドで関数を実行する( 実例 ):

#include <iostream>
#include <thread>
 
void foo(int a)
{
    std::cout << a << '\n';
}
 
int main()
{
    // Create and execute the thread
    std::thread thread(foo, 10); // foo is the function to execute, 10 is the
                                 // argument to pass to it
 
    // Keep going; the thread is executed separately
 
    // Wait for the thread to finish; we stay here until it is done
    thread.join();
 
    return 0;
}

メンバー関数の例 - 別のスレッドでメンバー関数を実行する( 実例 ):

#include <iostream>
#include <thread>
 
class Bar
{
public:
    void foo(int a)
    {
        std::cout << a << '\n';
    }
};
 
int main()
{
    Bar bar;
    
    // Create and execute the thread
    std::thread thread(&Bar::foo, &bar, 10); // Pass 10 to member function
 
    // The member function will be executed in a separate thread
 
    // Wait for the thread to finish, this is a blocking operation
    thread.join();
 
    return 0;
}

Functorオブジェクトの例( 実例 ):

#include <iostream>
#include <thread>
 
class Bar
{
public:
    void operator()(int a)
    {
        std::cout << a << '\n';
    }
};
 
int main()
{
    Bar bar;
    
    // Create and execute the thread
    std::thread thread(bar, 10); // Pass 10 to functor object
 
    // The functor object will be executed in a separate thread
 
    // Wait for the thread to finish, this is a blocking operation
    thread.join();
 
    return 0;
}

ラムダ式の例( 実例 ):

#include <iostream>
#include <thread>
 
int main()
{
    auto lambda = [](int a) { std::cout << a << '\n'; };

    // Create and execute the thread
    std::thread thread(lambda, 10); // Pass 10 to the lambda expression
 
    // The lambda expression will be executed in a separate thread
 
    // Wait for the thread to finish, this is a blocking operation
    thread.join();
 
    return 0;
}

現在のスレッドでの操作

std::this_threadは、呼び出された関数から現在のスレッド上で興味深いことをする関数を持つnamespaceです。

関数説明
get_id スレッドのIDを返します。
sleep_for 指定された時間だけ眠る
sleep_until 特定の時間まで眠る
yield 実行中のスレッドを再スケジュールし、他のスレッドに優先順位を与える

std::this_thread::get_idを使って現在のスレッドIDを取得する:

void foo()
{
    //Print this threads id
    std::cout << std::this_thread::get_id() << '\n';
}

std::thread thread{ foo };
thread.join(); //'threads' id has now been printed, should be something like 12556

foo(); //The id of the main thread is printed, should be something like 2420

std::this_thread::sleep_forを使って3秒間std::this_thread::sleep_for

void foo()
{
    std::this_thread::sleep_for(std::chrono::seconds(3));
}

std::thread thread{ foo };
foo.join();

std::cout << "Waited for 3 seconds!\n";

将来3時間までstd::this_thread::sleep_untilを使用してstd::this_thread::sleep_until

void foo()
{
    std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::hours(3));
}

std::thread thread{ foo };
thread.join();

std::cout << "We are now located 3 hours after the thread has been called\n";

他のスレッドにstd::this_thread::yieldを使って優先さstd::this_thread::yield

void foo(int a)
{
    for (int i = 0; i < al ++i)
        std::this_thread::yield(); //Now other threads take priority, because this thread
                                   //isn't doing anything important

    std::cout << "Hello World!\n";
}

std::thread thread{ foo, 10 };
thread.join();

std :: threadの代わりにstd :: asyncを使う

std::asyncはスレッドを作成することもできます。 std::threadと比較すると、あまり強力ではありませんが、非同期的に関数を実行したいときには使いやすくなります。

非同期に関数を呼び出す

#include <future>
#include <iostream>

unsigned int square(unsigned int i){
    return i*i;
}

int main() {
    auto f = std::async(std::launch::async, square, 8);
    std::cout << "square currently running\n"; //do something while square is running
    std::cout << "result is " << f.get() << '\n'; //getting the result from square
}

共通落とし穴

  • std::asyncは、関数によって計算される戻り値を保持するstd::futureを返します。そのfutureが破壊されると、スレッドが完了するまで待って、コードを効果的にシングルスレッドにします。これは、戻り値が不要なときに簡単に見落とされます。

    std::async(std::launch::async, square, 5);
    //thread already completed at this point, because the returning future got destroyed
    
  • std::asyncは起動ポリシーなしで動作するので、 std::async(square, 5);コンパイルする。そうすると、システムはスレッドを作成するかどうかを決定します。システムが効率的に実行できるよりも多くのスレッドを実行していない限り、システムはスレッドを作成することを選択しました。あいにく、実装は一般的にそのような状況でスレッドを作成しないことを選択するだけです。そのため、 std::launch::asyncを使用してその動作をオーバーライドして、システムにスレッドを強制的に作成させる必要があります。

  • 競合状態に注意してください。

先物と約束に関する非同期の詳細

スレッドが常に参加していることを保証する

std::threadデストラクタが呼び出されると、 join()またはdetach()いずれかの呼び出しが行われていなければなりません 。スレッドがstd::terminateされたりデタッチされていない場合、デフォルトではstd::terminateが呼び出されます。 RAIIを使用すると、これは一般的に次のように簡単に実行できます。

class thread_joiner
{
public:

    thread_joiner(std::thread t)
        : t_(std::move(t))
    { }

    ~thread_joiner()
    {
        if(t_.joinable()) {
            t_.join();
        }
    }

private:

    std::thread t_;
}

これは次のように使用されます:

 void perform_work()
 {
     // Perform some work
 }

 void t()
 {
     thread_joiner j{std::thread(perform_work)};
     // Do some other calculations while thread is running
 } // Thread is automatically joined here

これは例外安全性も提供します。スレッドを正常に作成し、他の計算を実行するt()行われた作業が例外をスローした場合、 join()は決してスレッドに呼び出されず、プロセスは終了します。

スレッドオブジェクトの再割り当て

私たちは空のスレッドオブジェクトを作成し、後でそれらに作業を割り当てることができます。

スレッドオブジェクトを別のアクティブなjoinableスレッドに割り当てると、スレッドが置き換えられる前にstd::terminateが自動的に呼び出されます。

#include <thread>

void foo()
{
    std::this_thread::sleep_for(std::chrono::seconds(3));
}
//create 100 thread objects that do nothing
std::thread executors[100];

// Some code

// I want to create some threads now

for (int i = 0;i < 100;i++)
{
    // If this object doesn't have a thread assigned
    if (!executors[i].joinable())
         executors[i] = std::thread(foo);
}

基本同期

スレッド同期は、他の同期プリミティブの中でも、ミューテックスを使用して達成できます。標準ライブラリにはいくつかのmutex型がありますが、最も単純なのはstd::mutexです。ミューテックスをロックするには、ロックを作成します。最も単純なロックタイプはstd::lock_guardです:

std::mutex m;
void worker() {
    std::lock_guard<std::mutex> guard(m); // Acquires a lock on the mutex
    // Synchronized code here
} // the mutex is automatically released when guard goes out of scope

std::lock_guardすると、mutexはロックオブジェクトのライフタイム全体にわたってロックされます。リージョンを手動でロックする必要がある場合は、代わりにstd::unique_lock使用します。

std::mutex m;
void worker() {
    // by default, constructing a unique_lock from a mutex will lock the mutex
    // by passing the std::defer_lock as a second argument, we
    // can construct the guard in an unlocked state instead and
    // manually lock later.
    std::unique_lock<std::mutex> guard(m, std::defer_lock);
    // the mutex is not locked yet!
    guard.lock();
    // critical section
    guard.unlock();
    // mutex is again released
}

より多くのスレッド同期構造

条件変数の使用

条件変数は、スレッド間の通信を調整するためにミューテックスと組み合わせて使用​​されるプリミティブです。これを達成するための排他的または最も効率的な方法ではありませんが、パターンに精通している人にとっては最も簡単な方法です。

1つはstd::unique_lock<std::mutex> std::condition_variableを待ちます。これにより、取得を続行するかどうかを決定する前に、コードが共有状態を安全に調べることができます。

以下は、 std::threadstd::condition_variablestd::mutexなどを使用してプロデューサ/コンシューマのスケッチを作成する方法です。

#include <condition_variable>
#include <cstddef>
#include <iostream>
#include <mutex>
#include <queue>
#include <random>
#include <thread>


int main()
{
    std::condition_variable cond;
    std::mutex mtx;
    std::queue<int> intq;
    bool stopped = false;

    std::thread producer{[&]()
    {
        // Prepare a random number generator.
        // Our producer will simply push random numbers to intq.
        //
        std::default_random_engine gen{};
        std::uniform_int_distribution<int> dist{};

        std::size_t count = 4006;    
        while(count--)
        {    
            // Always lock before changing
            // state guarded by a mutex and
            // condition_variable (a.k.a. "condvar").
            std::lock_guard<std::mutex> L{mtx};

            // Push a random int into the queue
            intq.push(dist(gen));

            // Tell the consumer it has an int
            cond.notify_one();
        }

        // All done.
        // Acquire the lock, set the stopped flag,
        // then inform the consumer.
        std::lock_guard<std::mutex> L{mtx};

        std::cout << "Producer is done!" << std::endl;

        stopped = true;
        cond.notify_one();
    }};

    std::thread consumer{[&]()
    {
        do{
            std::unique_lock<std::mutex> L{mtx};
            cond.wait(L,[&]()
            {
                // Acquire the lock only if
                // we've stopped or the queue
                // isn't empty
                return stopped || ! intq.empty();
            });

            // We own the mutex here; pop the queue
            // until it empties out.

            while( ! intq.empty())
            {
                const auto val = intq.front();
                intq.pop();

                std::cout << "Consumer popped: " << val << std::endl;
            }

            if(stopped){
                // producer has signaled a stop
                std::cout << "Consumer is done!" << std::endl;
                break;
            }

        }while(true);
    }};

    consumer.join();
    producer.join();
    
    std::cout << "Example Completed!" << std::endl;

    return 0;
}

単純なスレッドプールを作成する

C ++ 11スレッディングプリミティブは比較的低レベルです。それらは、スレッドプールのようなより高いレベルの構造体を書くのに使うことができます:

C ++ 14
struct tasks {
  // the mutex, condition variable and deque form a single
  // thread-safe triggered queue of tasks:
  std::mutex m;
  std::condition_variable v;
  // note that a packaged_task<void> can store a packaged_task<R>:
  std::deque<std::packaged_task<void()>> work;

  // this holds futures representing the worker threads being done:
  std::vector<std::future<void>> finished;

  // queue( lambda ) will enqueue the lambda into the tasks for the threads
  // to use.  A future of the type the lambda returns is given to let you get
  // the result out.
  template<class F, class R=std::result_of_t<F&()>>
  std::future<R> queue(F&& f) {
    // wrap the function object into a packaged task, splitting
    // execution from the return value:
    std::packaged_task<R()> p(std::forward<F>(f));

    auto r=p.get_future(); // get the return value before we hand off the task
    {
      std::unique_lock<std::mutex> l(m);
      work.emplace_back(std::move(p)); // store the task<R()> as a task<void()>
    }
    v.notify_one(); // wake a thread to work on the task

    return r; // return the future result of the task
  }

  // start N threads in the thread pool.
  void start(std::size_t N=1){
    for (std::size_t i = 0; i < N; ++i)
    {
      // each thread is a std::async running this->thread_task():
      finished.push_back(
        std::async(
          std::launch::async,
          [this]{ thread_task(); }
        )
      );
    }
  }
  // abort() cancels all non-started tasks, and tells every working thread
  // stop running, and waits for them to finish up.
  void abort() {
    cancel_pending();
    finish();
  }
  // cancel_pending() merely cancels all non-started tasks:
  void cancel_pending() {
    std::unique_lock<std::mutex> l(m);
    work.clear();
  }
  // finish enques a "stop the thread" message for every thread, then waits for them:
  void finish() {
    {
      std::unique_lock<std::mutex> l(m);
      for(auto&&unused:finished){
        work.push_back({});
      }
    }
    v.notify_all();
    finished.clear();
  }
  ~tasks() {
    finish();
  }
private:
  // the work that a worker thread does:
  void thread_task() {
    while(true){
      // pop a task off the queue:
      std::packaged_task<void()> f;
      {
        // usual thread-safe queue code:
        std::unique_lock<std::mutex> l(m);
        if (work.empty()){
          v.wait(l,[&]{return !work.empty();});
        }
        f = std::move(work.front());
        work.pop_front();
      }
      // if the task is invalid, it means we are asked to abort:
      if (!f.valid()) return;
      // otherwise, run the task:
      f();
    }
  }
};

tasks.queue( []{ return "hello world"s; } )は、 std::future<std::string>返しますtasks.queue( []{ return "hello world"s; } )これは、タスクオブジェクトが動作するまでにhello worldます。

tasks.start(10) (これは10個のスレッドを開始しますtasks.start(10)実行してスレッドを作成します。

packaged_task<void()>の使用は、移動のみの型を格納する型消去されたstd::function同等物がないためです。カスタムのものを書くことはおそらく、 packaged_task<void()>を使うよりも速くなるでしょう。

実例

C ++ 11

C ++ 11では、 result_of_t<blah>typename result_of<blah>::typeに置き換えます。

ミューテックスの詳細。

スレッドローカルストレージ

スレッドローカルストレージは、 thread_local キーワードを使用して作成できます。 thread_local指定子で宣言された変数は、 スレッド記憶期間を持つと言われます

  • プログラム内の各スレッドには、それぞれのスレッドローカル変数の独自のコピーがあります。
  • 関数(ローカル)スコープのスレッドローカル変数は、最初のコントロールがその定義を通過するときに初期化されます。そのような変数は、 extern宣言されていない限り、暗黙的に静的です。
  • 名前空間またはクラス(非ローカル)スコープを持つスレッドローカル変数は、スレッドの起動の一環として初期化されます。
  • スレッドローカル変数は、スレッド終了時に破棄されます。
  • クラスのメンバーは静的な場合にのみスレッドローカルにすることができます。そのため、(スレッド、インスタンス)のペアごとに1つのコピーではなく、スレッドごとにその変数のコピーが1つ存在します。

例:

void debug_counter() {
    thread_local int count = 0;
    Logger::log("This function has been called %d times by this thread", ++count);
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow