【C++同時およびマルチスレッド】std::threadクラス-マルチスレッドパラメータ

5299 ワード

スレッドパラメータ
通常のスレッドパラメータは簡単ですが、デフォルトでは、スレッド関数のパラメータが参照タイプであっても、パラメータはスレッド空間にコピーされ、スレッド実行体によってアクセスされます.上のスレッド空間は、スレッドがアクセスできる内部メモリです.次の例を見てみましょう.
void f(int i,std::string const& s);
std::thread t(f,3,”hello”);

fの2番目のパラメータが参照タイプであっても、文字列の文字面値「hello」はスレッドt空間内にコピーされ、std::stringタイプに変換される.上記の場合はエラーは発生しませんが、次のパラメータが自動変数を指すポインタの場合はエラーが発生しやすいです.
void f(int i,std::string const& s);
void oops(int some_param)
{
    char buffer[1024]; 
    sprintf(buffer, "%i",some_param); 
    std::thread t(f,3,buffer); 
    t.detach();
}

この場合、ポインタ変数bufferはスレッドt空間にコピーされ、この時点で関数oopsが終了し、bufferはstd::stringに変換されず、この時点で未定義の動作を引き起こす可能性が高い.ソリューションは次のとおりです.
void f(int i,std::string const& s);
void not_oops(int some_param)
{
    char buffer[1024]; 
    sprintf(buffer,"%i",some_param); 
    std::thread t(f,3,std::string(buffer)); 
    t.detach();
}

前述したように、プロセスパラメータはプロセスパス時に一度コピーされるため、プロセス関数パラメータを参照に設定しても、このコピーへの参照にすぎません.パラメータの操作は、パラメータを伝達する前の値を変更しません.次の例を見てください.
void update_data_for_widget(widget_id w,widget_data& data);
void oops_again(widget_id w)
{
    widget_data data;
    std::thread t(update_data_for_widget,w,data);
    display_status();
    t.join();
    process_widget_data(data);
}

スレッドtの実行が完了した後、dataの値は変更されません.process_widget_data(data)関数は、最初の値を処理します.std::refラップを使用して、リファレンス伝達を必要とするパラメータを参照して表示する必要があります.
void update_data_for_widget(widget_id w,widget_data& data);
void oops_again(widget_id w)
{
    widget_data data;
    std::thread t(update_data_for_widget,w,std::ref(data));
    display_status();
    t.join();
    process_widget_data(data);
}

コピー不可のパラメータを移動できる場合、例えばstd::unqiue_ptrオブジェクト、ソースオブジェクトが一時的であれば、移動操作は自動的に実行されます.ソースオブジェクトが名前付き変数の場合、std::move関数を明示的に呼び出す必要があります.
void process_big_object(std::unique_ptr);
std::unique_ptr p(new big_object);
p->prepare_data(42);
std::thread t(process_big_object,std::move(p));

スレッド所有権の移動
std::threadは移動可能で、コピー不可です.std::threadオブジェクト間でスレッド所有権を転送するにはsd::move関数を使用します.
void some_function();
void some_other_function();
std::thread t1(some_function);           // 1
std::thread t2=std::move(t1);            // 2
t1=std::thread(some_other_function);     // 3          std::move       
std::thread t3;                          // 4
t3=std::move(t2);                        // 5
t1=std::move(t3);                        // 6           
t1.detach();
t1=std::move(t3);                        // 7 ok

ここで、一時オブジェクトはstd::moveがスレッド所有権を暗黙的に呼び出すので、t 1=std::thread(some_other_function);呼び出しstd::moveを表示する必要はありません.threadオブジェクトを解析する必要がある場合は、join()の戻りまたはdetach()を待つ必要があります.同様に、スレッド所有権を移行する必要がある場合は、スレッドオブジェクトを受け入れる実行関数の完了を待つ必要があります.std::threadオブジェクトに新しい値を割り当てることで、スレッドを「破棄」することはできません.第6点において、t 1は依然としてsome_other_functionはもう一度連絡するので、t 3の所有権をt 1に直接渡すことはできません.
std::threadは移動をサポートし、スレッドの所有権が関数外で移行できることを意味します.
std::thread f()
{
  void some_function();
  return std::thread(some_function);
}

std::thread g()
{
  void some_other_function(int);
  std::thread t(some_other_function,42);
  return t;
}

所有権が関数内で伝達される場合、std::threadインスタンスはパラメータとして伝達されます.
void f(std::thread t);
void g()
{
  void some_function();
  f(std::thread(some_function));
  std::thread t(some_function);
  f(std::move(t));
}

この特性を利用して,スレッドオブジェクトのRAIDパッケージを実現できる.
class thread_guard
{
public:
    explicit thread_guard(std::thread &_t)
        : t(std::move(_t))
    {
        if (!t.joinable())
            throw std::logic_error("No Thread");
    }

    ~thread_guard()
    {
        if (t.joinable())
        {
            t.join();
        }
    }
    thread_guard(thread_guard const&) = delete;
    thread_guard& operator=(thread_guard const &) = delete;
private:
    std::thread t;
};
struct func;
void f() {
    int some_local_state;
    scoped_thread t(std::thread(func(some_local_state)));
    do_something_in_current_thread();
}

スレッドが移行できる特性を利用して、コンテナでスレッドを集中的に管理することができます.次のコードを見てください.
void do_work(unsigned id);
void f() {
    std::vector<:thread> threads;
    for(unsigned i=0;i<20;++i)
    {
        threads.push_back(std::thread(do_work,i));
    }
    std::for_each(threads.begin(),threads.end(),
                  std::mem_fn(&std::thread::join));
}

スレッド関連
スレッド数
std::thread::hardware_concurrency()関数は、マルチコアシステムでは一般的にコア数であるプログラム内で同時に実行可能なスレッド数を返します.しかし、この関数は単なるヒントであり、システム情報が取得できない場合、関数は0を返します.
スレッドの識別
スレッド識別タイプはstd::thread::idで、2つの方法で取得できます.
  • std::threadオブジェクトのメンバー関数get_を呼び出すid()を直接取得します.
  • 現在のスレッドでstdを呼び出す::this_thread::get_id()は、スレッド識別を取得することもできる.

  • 上のシナリオはスレッドsleepと似ています.上の同じフォーマットを使用して、get_id()関数をsleep()関数に置き換えればよい.std::thread::idオブジェクトは自由にコピーと比較できます.
  • 2 2 2つのオブジェクトのstd::thread::idが等しい場合、それらは同じスレッドであるか、または「スレッドなし」である.
  • が等しくなければ、2つの異なるスレッド、または1つはスレッドがあり、もう1つはスレッドがありません.

  • std::thread::idインスタンスは、特定のスレッドがいくつかの操作を行う必要があるかどうかを検出するためによく使用されます.これは、特定のスレッドが特定の操作を実行する必要があるシーンでよく使用されます.まず、これらのスレッドを見つけなければなりません.
    参照元:
    cppreference.com
    C++同時プログラミング1-マルチスレッドの管理を開始