マルチスレッドプログラミングで総和計算 - その3 Win32 API 編-

kawa0810 のブログ の続きです.
今回は Win32 API を用いたマルチスレッドプログラミング方法を取り扱います.

Win32 API を用いてマルチスレッドプログラミング

Win32 API にはスレッドを作成・実行するために以下のような関数が用意されています.また,各関数の詳細は msdn を参照してください.

CreateThread の注意点

msdn の説明を読むと CreateThread を使用する場合は以下の点に注意する必要があるそうです.

C のランタイムライブラリに記録されている関数を使うスレッドは、CreateThread 関数と ExitThread 関数ではなく、C のランタイム関数である beginthread 関数と endthread 関数を使うべきです。この方法に従わないと、ExitThread 関数を呼び出したときにわずかなメモリリークが発生します。

C のランタイムライブラリ(CRT)に記録されている関数とは "Win32 API ではない C の標準ライブラリのこと”をさしているそうです.また,日本語版の msdnメモリリークが発生するとなっていますがコチラ (CreateThread function (Windows)) をみると

If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.

「CRT はローメモリ状態のプロセスを終了するかもしれません」となっており,おそらく日本語の誤訳があったのではないかと思われます・・・
(参考: Cランタイムライブラリ - BIGLOBEなんでも相談室

いずれにせよ CreateThread よりも _beginthread か _beginthreadex のほうがよさそうですね.
ということで,今回は推奨されている _beginthreadex を使用します.

_beginthreadex を用いた総和計算

_beginthreadex を用いた総和計算の例を以下に記載します.

/**--------------------------
   beginthread_sum.cpp
   -----------------------*/

#include <Windows.h>
#include <process.h>//_beginthreadex 

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <numeric>

std::size_t const NUM_THREADS = 4;

template<class T>
struct Bthread_sum{
  std::size_t tid;
  std::size_t n;
  T const* x;
  T sum;
};

template<class T> static
unsigned __stdcall vec_sum_thread(void* arg){
  Bthread_sum<T>* param = static_cast<Bthread_sum<T>*>(arg);
  std::size_t const len = (param->n + NUM_THREADS - 1) / NUM_THREADS;
  std::size_t const beg = param->tid * len;
  std::size_t const end = (param->tid == NUM_THREADS - 1) ? param->n : beg+len;

  param->sum = 0;
  for(std::size_t i=beg; i<end; ++i){
    param->sum += param->x[i];
  }

  _endthreadex(0);
  return 0;
}

template<class T>
T vec_sum(std::size_t const n, T const* x){
  HANDLE hThread[NUM_THREADS];
  Bthread_sum<T> param[NUM_THREADS];
	
  for(std::size_t i=0; i<NUM_THREADS; ++i){
    param[i].tid = i;
    param[i].n = n;
    param[i].x = x;
    hThread[i] = (HANDLE)_beginthreadex(NULL, 0, vec_sum_thread<T>, &param[i], 0, NULL);
  }

  WaitForMultipleObjects(NUM_THREADS, hThread, TRUE, INFINITE);

  for(std::size_t i=1; i<NUM_THREADS; ++i)
    param[0].sum += param[i].sum;

  for(std::size_t i=0; i<NUM_THREADS; ++i)
    CloseHandle(hThread[i]);

  return param[0].sum;
}

int main(void){
  std::size_t const n = 1023;
  float* x;
  double* y;

  x = static_cast<float*>( malloc(n * sizeof(float)) );
  y = static_cast<double*>( malloc(n * sizeof(double)) );

  srand( static_cast<unsigned>(time(NULL)) );
  for(std::size_t i=0; i<n; ++i)
    x[i] = static_cast<float>( rand() ) / RAND_MAX;

  for(std::size_t i=0; i<n; ++i)
    y[i] = static_cast<double>( rand() ) / RAND_MAX;

  std::cout << "Single Precision" << std::endl;
  std::cout << "- Accumulate: " << std::accumulate(x, x+n, 0.0) << std::endl;
  std::cout << "- vec_add: " << vec_sum(n, x) << std::endl;

  std::cout << "\nDouble Precision" << std::endl;
  std::cout << "- Accumulate: " << std::accumulate(y, y+n, 0.0) << std::endl;
  std::cout << "- vec_add: " << vec_sum(n, y) << std::endl;
		 
  free(x);
  free(y);

  return 0;
}
マルチスレッドプログラミングで総和計算