クラスメンバ関数をCreateThreadで実行させるには?

解決


CC2008  2010-01-20 06:17:43  No: 71292

staticでないprivateメンバ関数をCreateThreadなどで実行させるにはどうしたらよいのでしょうか?
privateでなければstaticなメンバ関数をあいだに挟めばよいと思うのですが、privateだと上手くいきません。


επιστημη  URL  2010-01-20 06:55:25  No: 71293

- そのクラスのメソッド内でCreateThreadする。
- Threadに必要な引数をstructにまとめ、その中にthisも入れておき、
  friendしておく。


仲澤@失業者  2010-01-20 18:41:08  No: 71294

おこがましいですが、やや補足

-スレッド関数内では渡されたthis(つまりクラスのインスタンス)
  のメンバ関数(staticでないprivate関数)を呼べます。

スレッド関数をfriendにしてますからね。


επιστημη  URL  2010-01-20 19:38:56  No: 71295

↓ friendせんでもこれならおっけぃ。

#include <iostream>
#include <windows.h>

struct FactorialArgs;

class Factorial {
private:
  int result_;
  // スレッド本体
  void execute(int n) {
    int f = 1;
    for ( int i = 1; i < n; ++i ) {
      f *= i;
    }
    result_ = f;
  }
public:
  FactorialArgs* make_args(int n);
  static void run(void* v);
  int result() const { return result_; }
};

// スレッドに引き渡す引数
struct FactorialArgs {
  int n;
  Factorial* instance;
};

// ↑を作る
FactorialArgs* Factorial::make_args(int n) {
  FactorialArgs* result = new FactorialArgs();
  result->n = n;
  result->instance = this;
  return result;
}

// スレッド・エントリ 
// こっから private な Factorial::execute を呼ぶ
void Factorial::run(void* v) {
  FactorialArgs& args = *static_cast<FactorialArgs*>(v);
  args.instance->execute(args.n);
}

int main() {
  Factorial fact0;
  Factorial fact1;
  FactorialArgs* args0 = fact0.make_args(5);
  FactorialArgs* args1 = fact1.make_args(7);
  HANDLE handle[] = {
    CreateThread(NULL, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(&Factorial::run), args0, 0, NULL),
    CreateThread(NULL, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(&Factorial::run), args1, 0, NULL),
  };
  WaitForMultipleObjects(2, handle, TRUE, INFINITE);
  std::cout << "5! = " << fact0.result() << std::endl;
  std::cout << "7! = " << fact1.result() << std::endl;
  delete args0;
  delete args1;
}


tetrapod  2010-01-20 20:31:38  No: 71296

うーん、そのコードはたまたま偶然期待通りに動いているだけかと。
__stdcall (THREAD_START_ROUTINE) 規約と
__cdecl (static メンバ関数) 規約と
では、生成されるバイナリレベルでのコール・リターン手順が違うのでうまくない。
static void run(void*); を素直に THREAD_START_ROUTINE に適合するよう
static DWORD __stdcall run(void*); に修正して
不必要な reinterpret_cast は除去し
run の最後に return 0; でも追加しておくほうがよいと思う。


επιστημη  URL  2010-01-20 20:41:51  No: 71297

> コール・リターン手順が違うのでうまくない。

あー。んじゃもっぺん。

#include <iostream>
#include <windows.h>

struct FactorialArgs;

class Factorial {
private:
  int result_;
  // スレッド本体
  void execute(int n) {
    int f = 1;
    for ( int i = 1; i < n; ++i ) {
      f *= i;
    }
    result_ = f;
  }
public:
  FactorialArgs* make_args(int n);
  static DWORD __stdcall run(void* v);
  int result() const { return result_; }
};

// スレッドに引き渡す引数
struct FactorialArgs {
  int n;
  Factorial* instance;
};

// ↑を作る
FactorialArgs* Factorial::make_args(int n) {
  FactorialArgs* result = new FactorialArgs();
  result->n = n;
  result->instance = this;
  return result;
}

// スレッド・エントリ 
// こっから private な Factorial::execute を呼ぶ
DWORD __stdcall Factorial::run(void* v) {
  FactorialArgs& args = *static_cast<FactorialArgs*>(v);
  args.instance->execute(args.n);
  return 0;
}

int main() {
  Factorial fact0;
  Factorial fact1;
  FactorialArgs* args0 = fact0.make_args(5);
  FactorialArgs* args1 = fact1.make_args(7);
  HANDLE handle[] = {
    CreateThread(NULL, 0, &Factorial::run, args0, 0, NULL),
    CreateThread(NULL, 0, &Factorial::run, args1, 0, NULL),
  };
  WaitForMultipleObjects(2, handle, TRUE, INFINITE);
  std::cout << "5! = " << fact0.result() << std::endl;
  std::cout << "7! = " << fact1.result() << std::endl;
  delete args0;
  delete args1;
}


仲澤@失業者  2010-01-20 20:57:49  No: 71298

ほんとにCreateThread()で良いのだろうかという
心配もありますよね(vv;)。
例のCランタイムとMFCが利用できる、できないうんぬんという。


tetrapod  2010-01-20 21:23:42  No: 71299

あい。俺は CreateThread をそのまま使ったことはないっす。
後輩君にも常に _beginthread(ex) または AfxBeginThread を使うように指導しているです。

以下は蛇足
x86 は __stdcall, __cdecl, __thiscall が皆違うんだけど
x64 は実質 __fastcall 一択になったらしい。
x64 の setjmp/longjmp はアンワインドになるらしい・・・えらい違いだ。


CC2008  2010-01-23 07:28:45  No: 71300

皆さん、どうもありがとうございました。
とりあえず、friendを使ってみることにしました。

マルチスレッドは、正直難しいです……


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加