staticでないprivateメンバ関数をCreateThreadなどで実行させるにはどうしたらよいのでしょうか?
privateでなければstaticなメンバ関数をあいだに挟めばよいと思うのですが、privateだと上手くいきません。
- そのクラスのメソッド内でCreateThreadする。
- Threadに必要な引数をstructにまとめ、その中にthisも入れておき、
friendしておく。
おこがましいですが、やや補足
-スレッド関数内では渡されたthis(つまりクラスのインスタンス)
のメンバ関数(staticでないprivate関数)を呼べます。
スレッド関数をfriendにしてますからね。
↓ 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;
}
うーん、そのコードはたまたま偶然期待通りに動いているだけかと。
__stdcall (THREAD_START_ROUTINE) 規約と
__cdecl (static メンバ関数) 規約と
では、生成されるバイナリレベルでのコール・リターン手順が違うのでうまくない。
static void run(void*); を素直に THREAD_START_ROUTINE に適合するよう
static DWORD __stdcall run(void*); に修正して
不必要な reinterpret_cast は除去し
run の最後に return 0; でも追加しておくほうがよいと思う。
> コール・リターン手順が違うのでうまくない。
あー。んじゃもっぺん。
#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;
}
ほんとにCreateThread()で良いのだろうかという
心配もありますよね(vv;)。
例のCランタイムとMFCが利用できる、できないうんぬんという。
あい。俺は CreateThread をそのまま使ったことはないっす。
後輩君にも常に _beginthread(ex) または AfxBeginThread を使うように指導しているです。
以下は蛇足
x86 は __stdcall, __cdecl, __thiscall が皆違うんだけど
x64 は実質 __fastcall 一択になったらしい。
x64 の setjmp/longjmp はアンワインドになるらしい・・・えらい違いだ。
皆さん、どうもありがとうございました。
とりあえず、friendを使ってみることにしました。
マルチスレッドは、正直難しいです……