深夜開発

プログラミング備忘録

Win32 threadを使ったスレッド生成

Win32API

Microsoft Windows上で用いられるAPIで、スレッドに限らずあらゆる機能を提供する。 通常スレッドを生成するにはCreateThread関数を使用するが、諸事情で同じ機能を提供する_beginThread関数、AfxBeginThread関数なども存在する

Sample code

#include <stdio.h>
#include <windows.h>

#define STACK_SIZE (512 * 1024)

struct thread_args
{
    int r;
    int a;
    int b;
    int c;
}

// 生成したスレッドで実行する関数
UINT thread_entry(void *args) {
    auto targs = reinterpret_cast<thread_args*>(args);
    targs->r = targs->a * targs->b + targs->c;

    return 0;
}

int main()
{
    thread_arg args;
    args.r = 0;
    args.a = 10;
    args.b = 20;
    args.c = 30;

    UINT thread_id = 0;
    auto thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(
        nullptr,                               // セキュリティ属性
        static_cast<unsigned int>(STACK_SIZE), // スタックサイズ
        thread_entry,                          // スレッドの実行を開始する関数
        &args,                                 // スレッド関数に渡される引数
        CREATE_SUSPENDED,                      // 初期化フラグ。『CREATE_SUSPENDED』を指定すると一時停止状態で生成される
        &thread_id                             // スレッド識別子
    ));

    // スレッド優先度を設定
    // 『THREAD_PRIORITY_ABOVE_NORMAL』は通常より1ポイント上の優先順位
    SetThreadPriority(thread_handle, THREAD_PRIORITY_ABOVE_NORMAL);

    // スレッドの実行を開始する
    ResumeThread(thread_handle);

    // スレッドの終了を待つ
    WaitForSingleObject(thread_handle, INFINITE);

    // スレッドハンドルを閉じる
    CloseHandle(thread_handle);

    // 結果を出力
    printf("value = %d\n", args.r);

    return 0;
}

出力

value = 230

std::threadを使ったスレッド生成

std::thread

C++11より導入された、新しい実行スレッドを生成、待機、その他の操作を行う仕組みを提供するテンプレートクラス。 std::threadのコンストラクタに与えられた関数オブジェクトと、このコンストラクタを呼び出したスレッドとの間で並行に処理が実行される。

コンストラクタの定義は、

template <class F, class ...Args>
explicit thread(F&& f, Args&&... args);

このような可変引数の形になっていて、ラムダ式と共に用いられる場合が多い。

機能は最小限のものしか提供されない為、実装環境に依存したスレッドハンドルを取得するメソッドが提供されており、

native_handle_type native_handle();

このハンドルを使う事で高度なスレッド操作を行うことが出来る。 通常このハンドルは、

  • Unix系環境(libstdc++、libc++)では『pthread_t』
  • MS Windows環境(Visual C++)では『HANDLE』

を表している。 各実装環境でのスレッド生成は以下を参照

kamai-tech-lab.hatenablog.com kamai-tech-lab.hatenablog.com

Sample code

#include <stdio.h>
#include <thread>

int main()
{
    int value = 0;

    // スレッドを作成、発行
    // ここで生成したスレッドの返り値は無視されるため、結果を受け取る変数の参照も渡しておく
    auto mad = std::thread([](int &r, int a, int b, int c)
    {
        r = a * b + c;
    }, std::ref(value), 10, 20, 30);


#ifdef WIN32
    // スレッド優先度を設定
    // 『THREAD_PRIORITY_ABOVE_NORMAL』は通常より1ポイント下の優先順位
    SetThreadPriority(mad.native_handle(), THREAD_PRIORITY_BELOW_NORMAL);
#elif defined(__linux__)
    sched_param param;
    param.sched_priority = 10;

    // スケジューリングポリシー、優先度を設定
    pthread_setschedparam(mad.native_handle(), SCHED_RR, &param);
#endif

    // スレッドが完了するまで待つ
    mad.join();

    // 結果を出力
    printf("value = %d\n", value);
    
    return 0;
}
出力
value = 230

pthreadを使ったスレッド生成

pthread

一般的にPOSIXスレッドの標準実装ライブラリの事を指し、スレッドの生成や基本的な操作を行うことが出来る。 POSIXをサポートしている多くの環境(主にUnix系)で利用できる反面、標準では基本的な機能のみが提供されるため、環境に依存した高度な処理を行う場合には拡張命令を使用する必要がある。

Sample code

#include <stdio.h>
#include <pthread.h>

#define STACK_SIZE (512 * 1024)
#define SCHED_POLICY SCHED_OTHER
#define SCHED_PRIORITY 10

#define CLAMP(x, min_value, max_value) (x < min_value ? min_value : (max_value < x ? max_value : x))

struct thread_arg
{
    int r;
    int a;
    int b;
    int c;
};

// 生成したスレッドで実行する関数
void* thread_entry(void *args)
{
    auto targs = reinterpret_cast<thread_arg*>(args);
    targs->r = targs->a * targs->b + targs->c;

    return nullptr;
}

int main()
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);

    // 生成するスレッドのスタックサイズを設定
    if (pthread_attr_setstacksize(&attr, STACK_SIZE) != 0) {
        pthread_attr_destroy(&attr);
        return 1;
    }

    // 呼び出しスレッドのスケジューリング属性を継承しないよう設定
    if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0) {
        pthread_attr_destroy(&attr);
        return 2;
    }

    // スケジューリングポリシーを設定
    if (pthread_attr_setschedpolicy(&attr, SCHED_POLICY) != 0) {
        pthread_attr_destroy(&attr);
        return 3;
    }

    sched_param param;

    const int min_priority = sched_get_priority_min(SCHED_POLICY);
    const int max_priority = sched_get_priority_max(SCHED_POLICY);
    param.sched_priority = CLAMP(SCHED_PRIORITY, min_priority, max_priority);

    // スケジューリング優先度を設定
    if (pthread_attr_setschedparam(&attr, &param) != 0) {
        pthread_attr_destroy(&attr);
        return 4;
    }
    
    thread_arg args;
    args.r = 0;
    args.a = 10;
    args.b = 20;
    args.c = 30;

    // スレッドのハンドル
    pthread_t thread;

    // スレッドを生成、発行
    if (pthread_create(&thread, &attr, thread_entry, &args) != 0) {
        pthread_attr_destroy(&attr);
        return 5;
    }

    // スレッド属性を破棄
    pthread_attr_destroy(&attr);

    // スレッドの終了を待つ
    // スレッドの内部メモリを解放する為、pthread_join または pthread_detach を必ず呼ばなければならない
    pthread_join(thread, nullptr);

    // 結果を出力
    printf("value = %d\n", args.r);

    return 0;
}
出力
value = 230