マルチスレッドの仕樣について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

マルチスレッドの仕樣について

#1

投稿記事 by MoNoQLoREATOR » 7年前

現在マルチスレッドについて調べています。
このサイトによると、スレッドの処理が終了してもスレッドオブジェクトは自動で解放されないようです。
ただ、「マルチスレッドは親プロセスのメモリ空間を共有します」とあるため、WinMain関数が終了し、プログラム自体がシステムから削除された場合はそれに伴いスレッドオブジェクトも削除されるものと思われます。
まず、この解釈で合っているでしょうか?
そうならば、プログラム終了時「走っているスレッドを見つけて終了させて解放して」なんていう茶番をせずに済みますね。

次に、スレッドの処理が終了した後スレッドオブジェクトを解放する際のお話をします。
いくらプログラムが終了したら解放されると言っても、スレッドを作り続けて1つも解放しないというのは良くないでしょう。
一番簡単なのは、スレッドの処理の最後で自分自身を解放してしまう方法です。
実験してみました。

main.cpp

コード:

#include "thread.h"
#include "stdio.h"

fw_thread_ func(void * param){
	printf("start");
	CloseHandle(param);
	printf("closed");
	return 1;
}

int main(){

	fw::thread t(func, false);
	t.begin(t.gethandle() );

	return(0);
}
thread.h

コード:

#pragma once
#include <windows.h>
#include <stdexcept>

#define fw_thread_ DWORD WINAPI

namespace fw{

class thread{
	HANDLE handle;
	DWORD Result;
	LPTHREAD_START_ROUTINE myfunc;
	void * myparam;

	void nullet(){
		handle = NULL;
		myfunc = NULL;
		myparam = NULL;
	}

public:

	bool begin(LPTHREAD_START_ROUTINE func, void * param = NULL){
		void * p = param;
		if(p==NULL) p = myparam;
		if(func==NULL){
			#ifdef FW_THREAD_POP_UP_
			fw::popup("呼び出す関数が指定されていないか、もしくは無効です","fw::threadエラー");
			#endif
			throw std::invalid_argument("fw::thread_func_error");
		}

		handle = CreateThread( NULL, 0, func, param, 0, LPDWORD() );
		return true;
	}
	bool begin(void * param){
		return begin(myfunc, param);
	}
	bool begin(){ return begin(myfunc, myparam); }

	thread(){ nullet(); }

	void set(LPTHREAD_START_ROUTINE func, void * param){
		nullet();
		myfunc = func;
		myparam = param;
	}
	thread(LPTHREAD_START_ROUTINE func, void * param, bool flag=true){
		set(func, param);
		if(flag) begin();
	}

	void set(LPTHREAD_START_ROUTINE func){
		nullet();
		myfunc = func;
	}
	thread(LPTHREAD_START_ROUTINE func, bool flag=true){
		set(func);
		if(flag) begin();
	}

	void set(void * param){
		nullet();
		myparam = param;
	}
	thread(void * param){ set(param); }

	bool working(){
		GetExitCodeThread(handle , &Result);
		if(Result == STILL_ACTIVE) return true;

		CloseHandle(handle);
		return false;
	}
	bool working(unsigned long & target){
		if(working() ) return true;

		target = Result;
		return false;
	}
	bool resting(){
		return !working();
	}

	//*
	void * gethandle() const { return handle; }
	//*/

	unsigned long result(){ return Result; }
};

}
結果。startは表示されましたがclosedは表示されませんでした。
ということは、少なくともCloseHandle関数を呼び出すとスレッド処理が終了する(もしくは停止する)ということですね。
ただ、スレッドオブジェクトは解放されているのかという肝心な部分は確かめようがなかったですね。
実際のところどうなのでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: マルチスレッドの仕樣について

#2

投稿記事 by softya(ソフト屋) » 7年前

VC++だと_beginthreadを使わないとメモリリークが起きるかも知れません。
あと、CloseHandle(param);で閉じるとスレッド関数内に生成したクラスのインスタンスのデストラクタが呼ばれないので色々まずいと思います。
_endthread()で閉じてもデストラクタは呼ばれないので素直にreturnするべきでしょう。そうすればスレッドハンドルは閉じます。

※ 私は日頃はスレッドはMFCでしか書いていないので通常のc++だと別のワナがあるかも知れません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: マルチスレッドの仕樣について

#3

投稿記事 by YuO » 7年前

スレッドオブジェクトは,
  • スレッドが終了している
  • 自身を参照する,開いているスレッドハンドルが一つも無い
の二つの条件を満たすと破棄されます。
いくらCloseHandleしても,実行中のスレッドオブジェクトは破棄されず,スレッドが中断されることもありません。
スレッドハンドルはスレッドオブジェクトを操作するための物であって,スレッドオブジェクトそのものでも,スレッドそのものでもありませんから。

さて,今回closedが表示されなかったのは,「たまたま」でしょう。
作られたスレッドがメインスレッドの実行とどうスケジュールされるかで結果が変わります。
メインスレッドが終了する前にclosedまでいけば表示されたでしょうし,
そもそもstartすら表示されない可能性も大いにあります。

例えば,mainがreturnする直前に,Sleep(1000);とか入れれば,closedまで表示されると思われます。
# CRT使用時にCreateThreadではなく_beginthread(ex)使うべき,というのはsoftya(ソフト屋)さんの書かれている通り。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: マルチスレッドの仕樣について

#4

投稿記事 by softya(ソフト屋) » 7年前

よく見たら、スレッドの終了をまたずにmain が終わってるんですね。
それじゃ"close"が表示されないのはプロセスが先に終了したせいかも知れません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#5

投稿記事 by MoNoQLoREATOR » 7年前

ソフト屋さん、YuOさん 返信ありがとうございます。
_beginthread, _beginthreadex についてはこちらで調べて理解しました。
使い捨てしたいときは_beginthread、ハンドルを使って操作したいときは_beginthreadexを使うと良さそうですね。
この方針でスレッド管理クラスを改良することにします。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#6

投稿記事 by MoNoQLoREATOR » 7年前

スレッドが終了するまで待つようにしてみると、ちゃんとclosedまで表示されました。

コード:

#include "thread.h"
#include "stdio.h"

fw_thread_ func(void * param){
	printf("start\n");
	CloseHandle(param);
	printf("closed\n");
	return 1;
}

int main(){

	fw::thread t(func, false);
	t.begin(t.gethandle() );

	while(t.working() ){}

	return(0);
}

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#7

投稿記事 by MoNoQLoREATOR » 7年前

_beginthreadex関数の第3引数に unsigned (*function)(void*) を渡したところ、

コード:

error C2664: '_beginthreadex' : 3 番目の引数を 'unsigned int (__cdecl *)(void *)' から 'unsigned int (__stdcall *)(void *)' に変換できません。(新しい機能 ; ヘルプを参照)
        この変換には reinterpret_cast, C スタイル キャストまたは関数スタイルのキャストが必要です。
と言われてしまったため、 unsigned __stdcall(*function)(void*) に変更してみたのですが、関数ポインタを宣言する段階で蹴られてしまいました。

ソースコード

コード:

#pragma once
#include <stdexcept>
#include <process.h>

#define fw_thread_ unsigned __stdcall

namespace fw{

	inline void newthread(void(* function)(void*), void * parameter){ _beginthread(function, 0, parameter); }

class thread{
	uintptr_t handle;
	DWORD Result;
	unsigned __stdcall(*Function)(void*);
	void * Parameter;

	void nullet(){
		handle = 0;
		Function = NULL;
		Parameter = NULL;
	}

public:

	thread(){ nullet(); }

	void set(unsigned __stdcall(*function)(void*), void * parameter){
		Function = function;
		Parameter = parameter;
	}
	thread(unsigned __stdcall(*function)(void*), void * parameter){
		handle = 0;
		Function = function;
		Parameter = parameter;
	}

	void set(unsigned __stdcall(*function)(void*) ){ Function = function; }
	thread(unsigned __stdcall(*function)(void*) ){
		nullet();
		Function = function;
	}

	void set(void * parameter){ Parameter = parameter; }
	thread(void * parameter){
		nullet();
		Parameter = parameter;
	}

	bool begin(unsigned __stdcall(*function)(void*), void * parameter){
		if(handle != 0) CloseHandle( (HANDLE)handle);
		if(function == NULL){
			#ifdef FW_THREAD_POP_UP_
			fw::popup("呼び出す関数が指定されていないか、もしくは無効です","fw::threadエラー");
			#endif
			throw std::invalid_argument("fw::thread_func_error");
		}

		handle = _beginthreadex(NULL, 0, function, parameter, 0, NULL);
		return handle!=0;
	}
	bool begin(unsigned __stdcall(*function)(void*) ){ return begin(function, Parameter); }
	bool begin(void * parameter){ return begin(Function, parameter); }
	bool begin(){ return begin(Function, Parameter); }

	bool working(){
		GetExitCodeThread( (HANDLE)handle, &Result);
		if(Result == STILL_ACTIVE) return true;
		return false;
	}
	bool working(unsigned long & target){
		if(working() ) return true;

		target = Result;
		return false;
	}
	bool resting(){ return !working(); }

	/*
	void * gethandle() const { return handle; }
	//*/

	unsigned long result(){ return Result; }
};

}
エラー文

コード:

------ ビルド開始: プロジェクト: fireworksProject, 構成: Debug Win32 ------
コンパイルしています...
fireworks.cpp
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(14) : error C2059: 構文エラー : '('
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(14) : error C2238: ';' の前に無効なトークンがあります。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(27) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(27) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(31) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(31) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(37) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(37) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(38) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(38) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(49) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(49) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(61) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(61) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(19) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(28) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(28) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(33) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(33) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(37) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(37) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(40) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(40) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(51) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(58) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(61) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(62) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(63) : error C2065: 'Function' : 定義されていない識別子です。
test.cpp
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(14) : error C2059: 構文エラー : '('
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(14) : error C2238: ';' の前に無効なトークンがあります。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(27) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(27) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(31) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(31) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(37) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(37) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(38) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(38) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(49) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(49) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(61) : error C2059: 構文エラー : '*'
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(61) : error C2091: 関数は関数を返せません。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(19) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(28) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(28) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(33) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(33) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(37) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(37) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(40) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(40) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(51) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(58) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(61) : error C2065: 'function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(62) : error C2065: 'Function' : 定義されていない識別子です。
c:\documents and settings\administrator\my documents\visual studio 2008\projects\fireworks\standardedition\thread.h(63) : error C2065: 'Function' : 定義されていない識別子です。
コードを生成中...
ビルドログは "file://c:\Documents and Settings\Administrator\My Documents\Visual Studio 2008\Projects\fireworksProject\Debug\BuildLog.htm" に保存されました。
fireworksProject - エラー 56、警告 0
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========
何故なのでしょうか?

beatle
記事: 1280
登録日時: 8年前
住所: 埼玉
連絡を取る:

Re: マルチスレッドの仕樣について

#8

投稿記事 by beatle » 7年前

コード:

unsigned __stdcall(*function)(void*)
ではなく

コード:

unsigned (__stdcall *function)(void*)
としたらどうでしょう。エラーメッセージを見ただけの想像なので違うかもしれません。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#9

投稿記事 by MoNoQLoREATOR » 7年前

beatleさんありがとうございます。
その通りに変更してみるとエラーが出なくなりました。

コード:

#pragma once
#include <stdexcept>
#include <process.h>

#define fw_thread_ unsigned __stdcall

namespace fw{

	inline void newthread(void(* function)(void*), void * parameter){ _beginthread(function, 0, parameter); }

class thread{
	uintptr_t handle;
	DWORD Result;
	unsigned (__stdcall * Function)(void*);
	void * Parameter;

	void nullet(){
		handle = 0;
		Function = NULL;
		Parameter = NULL;
	}

public:

	thread(){ nullet(); }

	void set(unsigned (__stdcall * function)(void*), void * parameter){
		Function = function;
		Parameter = parameter;
	}
	thread(unsigned (__stdcall * function)(void*), void * parameter){
		handle = 0;
		Function = function;
		Parameter = parameter;
	}

	void set(unsigned (__stdcall * function)(void*) ){ Function = function; }
	thread(unsigned (__stdcall * function)(void*) ){
		nullet();
		Function = function;
	}

	void set(void * parameter){ Parameter = parameter; }
	thread(void * parameter){
		nullet();
		Parameter = parameter;
	}

	bool begin(unsigned (__stdcall * function)(void*), void * parameter){
		close();
		if(function == NULL){
			#ifdef FW_THREAD_POP_UP_
			fw::popup("呼び出す関数が指定されていないか、もしくは無効です","fw::threadエラー");
			#endif
			throw std::invalid_argument("fw::thread_func_error");
		}

		handle = _beginthreadex(NULL, 0, function, parameter, 0, NULL);
		return handle!=0;
	}
	bool begin(unsigned (__stdcall * function)(void*) ){ return begin(function, Parameter); }
	bool begin(void * parameter){ return begin(Function, parameter); }
	bool begin(){ return begin(Function, Parameter); }

	bool working(){
		GetExitCodeThread( (HANDLE)handle, &Result);
		if(Result == STILL_ACTIVE) return true;
		return false;
	}
	bool working(unsigned long & target){
		if(working() ) return true;

		target = Result;
		return false;
	}
	bool resting(){ return !working(); }

	/*
	void * gethandle() const { return handle; }
	//*/

	unsigned long result() const { return Result; }

	void close(){ if(handle != 0) CloseHandle( (HANDLE)handle); }
	~thread(){ close(); }
};

}

そうだ。忘れていました。
_beginthreadex関数から返ってくるハンドルは uintptr_t型(unsigned int型)なのに、CloseHandle関数やGetExitCodeThread関数に渡すハンドルは HANDLE型(void * 型)となっています。
単純に (HANDLE) という方法でキャストしたのではキャストした変数のアドレスを渡しているだけなのですから、これでは駄目なのではないでしょうか?
そもそも_beginthreadex関数の戻値が uintptr_t型(unsigned int型)であること自体がおかしいと思うのですが。

アバター
御津凪
管理人
記事: 200
登録日時: 9年前
住所: 道内
連絡を取る:

Re: マルチスレッドの仕樣について

#10

投稿記事 by 御津凪 » 7年前

単純に言えば _beginthreadex 関数等は所謂環境依存の関数であるため、戻り値も環境依存の値になります。(実装も環境依存なので実装されていないこともある)
実際に _beginthreadex 関数の実装を VisualStudio に付属するCRTソースコードを見ると、 CreateThread の戻り値をキャストして返しています。
なので、 Windows での実装はこの方法で正しいです。

※戻り値が HANDLE 型でないのは、Cランタイムライブラリ内に含まれているからで、 HANDLE という Windows 独自の型を定義している箇所がないためで、代わりに型の表現として一番近い uintptr_t 型となっています
This article was written by "Mitsunagi".

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#11

投稿記事 by MoNoQLoREATOR » 7年前

御津凪さん返信ありがとうございます。
戻値がHANDLE型でない理由はわかりますが、単純に void * 型で返せばよかったのでは?と思いますが。
HANDLE型の正体も環境依存なのでしょうか。

そして、CloseHandle関数やGetExitCodeThread関数に渡す際は (HANDLE) としてキャストすれば良いのでしょうか?

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#12

投稿記事 by MoNoQLoREATOR » 7年前

(HANDLE) としてキャストした方法では、GetExitCodeThread関数を呼んで受け取った情報が常に「スレッドが動いている」でした。
やはりこれでは駄目なのではないでしょうか。
次に、reinterpret_castを使用して、戻値の値をアドレスとみなして void *型変数に代入し、それを渡してみました。

コード:

#include "thread.h"
#include "view.h"
#include "DxLib.h"

fw::view v;

fw_thread_ function(void*){
	while(CheckHitKey(KEY_INPUT_A)==0){}
	v.add("EndThread...");
	return 1;
}

int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpC, int nC){
	ChangeWindowMode(true);
	SetOutApplicationLogValidFlag(false);
	SetWindowText("DxLib");
	SetMultiThreadFlag(true);
	if(DxLib_Init() == -1) return(-1);
	SetAlwaysRunFlag(true);
	SetDrawScreen(DX_SCREEN_BACK);
	SetDragFileValidFlag(true);

	fw::thread t(function);
	t.begin();
	SetFontSize(30);

	while(ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0){
        ClsDrawScreen();

		if(t.resting() ) DrawString(0,0, "finish", 0xff0000);
		if(CheckHitKey(KEY_INPUT_A) ) DrawString(0,32, "pressed", 0xffffff);

		ScreenFlip();
	}

	DxLib_End();
	return(0);
}
thread.h

コード:

#pragma once
#include <stdexcept>
#include <process.h>

#define fw_thread_ unsigned __stdcall

namespace fw{

	inline void newthread(void(* function)(void*), void * parameter){ _beginthread(function, 0, parameter); }

class thread{
	void * handle;
	DWORD Result;
	unsigned (__stdcall * Function)(void*);
	void * Parameter;

	void nullet(){
		handle = NULL;
		Function = NULL;
		Parameter = NULL;
	}

public:

	thread(){ nullet(); }

	void set(unsigned (__stdcall * function)(void*), void * parameter){
		Function = function;
		Parameter = parameter;
	}
	thread(unsigned (__stdcall * function)(void*), void * parameter){
		handle = 0;
		Function = function;
		Parameter = parameter;
	}

	void set(unsigned (__stdcall * function)(void*) ){ Function = function; }
	thread(unsigned (__stdcall * function)(void*) ){
		nullet();
		Function = function;
	}

	void set(void * parameter){ Parameter = parameter; }
	thread(void * parameter){
		nullet();
		Parameter = parameter;
	}

	bool begin(unsigned (__stdcall * function)(void*), void * parameter){
		close();
		if(function == NULL){
			#ifdef FW_THREAD_POP_UP_
			fw::popup("呼び出す関数が指定されていないか、もしくは無効です","fw::threadエラー");
			#endif
			throw std::invalid_argument("fw::thread_func_error");
		}

		uintptr_t h = _beginthreadex(NULL, 0, function, parameter, 0, NULL);
		handle = reinterpret_cast<void *>(h);
		return handle!=NULL;
	}
	bool begin(unsigned (__stdcall * function)(void*) ){ return begin(function, Parameter); }
	bool begin(void * parameter){ return begin(Function, parameter); }
	bool begin(){ return begin(Function, Parameter); }

	bool working(){
		if(handle==NULL){
			#ifdef FW_THREAD_POP_UP_
			fw::popup("スレッドの生成に失敗したか、もしくはbegin関数が呼ばれていません","fw::threadエラー");
			#endif
			throw std::invalid_argument("fw::thread_handle_error");
		}
		GetExitCodeThread(handle, &Result);
		if(Result == STILL_ACTIVE) return true;
		return false;
	}
	bool working(unsigned long & target){
		if(working() ) return true;

		target = Result;
		return false;
	}
	bool resting(){ return !working(); }

	unsigned long result() const { return Result; }

	void close(){ if(handle != 0) CloseHandle(handle); }
	~thread(){ close(); }
};

}
viewクラスは新しくウインドウを作成し、文字列を表示するものです。
add関数で 表示する文字列 を追加します。

スレッドはAキーが押されるとviewクラスが作成したウインドウに"EndThread..."という文字列を追加して処理を終了します。
WinMain関数ではスレッドが終了されると、以後"finish"という文字列が描画されるようになります。

結果。
viewクラスが作成したウインドウにEndThread...が表示された後もfinishは表示されませんでした。
つまり、情報を正確に取得できていないということですね。
GetExitCodeThread関数は処理に失敗すると0を返すそうですが、0は返していませんでしたし、受け取り側の変数にはしっかりと「スレッド実行中を表す定数」が代入されていました。

_beginthreadex関数の戻値はどのように取扱えば良いのでしょうか?

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#13

投稿記事 by MoNoQLoREATOR » 7年前

すみません。
ちゃんと取得できていました。
DxLibさんは独自の色コードを使用しているらしく、0xff0000という指定の仕方では赤色にならず、黒色になるようです。
というわけで、正しく色コードを指定してあげたらちゃんと表示されました=正しく情報を取得することができていました。

解決しました。
本当にありがとうございました。

アバター
へにっくす
記事: 630
登録日時: 8年前
住所: 東京都

Re: マルチスレッドの仕樣について

#14

投稿記事 by へにっくす » 7年前

もう見ていないかもしれませんが、、
MoNoQLoREATOR さんが書きました:DxLibさんは独自の色コードを使用しているらしく、0xff0000という指定の仕方では赤色にならず、黒色になるようです。
何を持って独自の色と判定したのでしょうね?
Windowsの色指定(COLORREF型)では、0xFF0000は青色なんですが。
COLORREF型について
HTMLの色指定ならば赤で正解です。
HTML色見本
一番いいのは、DxLibリファレンスにある、GetColor関数を使うことですね。
リファレンス-GetColor関数
解決コードがないので、とりあえず気になったことを投稿しましたが、ちゃんと上記のことを理解しているなら、よけいなお世話でした
written by へにっくす

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#15

投稿記事 by MoNoQLoREATOR » 7年前

>>へにっくすさん
わざわざ返信ありがとうございます。

0xff0000と指定したのに黒色で表示されたからDXライブラリさんは独自の色コードを使用していると判定したのですがどこかおかしかったでしょうか?Windowsの色指定とも違うようですし。
以前にも、この指定方法で指定して想定通りの色で表示されなかったことがありましたし。

へにっくすさんがお仰る通り、素直にGetColor関数を使用するのが良さそうですね。

解決コードは既に貼られていますよ。
正しく動作しているか確認するためのコードが間違っていたというだけです。
それとも、動作確認用に使用したコードも必ず提示しなければならないルールでしたっけ?

アバター
へにっくす
記事: 630
登録日時: 8年前
住所: 東京都

Re: マルチスレッドの仕樣について

#16

投稿記事 by へにっくす » 7年前

MoNoQLoREATOR さんが書きました:0xff0000と指定したのに黒色で表示されたからDXライブラリさんは独自の色コードを使用していると判定したのですがどこかおかしかったでしょうか?
これは私の書き方がおかしいですね。
何で0xff0000と指定したのかな?と聞きたかったんだけど、、まあいいや。
MoNoQLoREATOR さんが書きました:解決コードは既に貼られていますよ。
貼られたコードにはGetColor関数を使っていないよね?
動作確認用も含めて直したものを普通は貼り付けるものかと思ってましたが。。
失礼しました。
written by へにっくす

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: マルチスレッドの仕樣について

#17

投稿記事 by ISLe » 7年前

DXライブラリの色コードというかGetColor関数が返す値は、画面モードに最適化されたピクセル情報としての値です。
なので実行時のピクセル形式に依存します。

そのため外部変数の初期化でGetColor関数を使うなど、DxLib_Init関数を呼び出して画面モードが定まる前にGetColor関数を呼び出すと期待する色と異なる場合があります。
ちなみに、このことは、この掲示板で過去に何度も話題に上がっています。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#18

投稿記事 by MoNoQLoREATOR » 7年前

へにっくす さんが書きました:
MoNoQLoREATOR さんが書きました:0xff0000と指定したのに黒色で表示されたからDXライブラリさんは独自の色コードを使用していると判定したのですがどこかおかしかったでしょうか?
これは私の書き方がおかしいですね。
何で0xff0000と指定したのかな?と聞きたかったんだけど、、まあいいや。
GetColor~と書くよりもこっちの形式で書いた方が速いからです。
へにっくす さんが書きました:
MoNoQLoREATOR さんが書きました:解決コードは既に貼られていますよ。
貼られたコードにはGetColor関数を使っていないよね?
動作確認用も含めて直したものを普通は貼り付けるものかと思ってましたが。。
失礼しました。
まあ、動作確認用コードを張ることに意味が有ると思うか無いと思うかは人それぞれですからね。
私は無いと思います。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: マルチスレッドの仕樣について

#19

投稿記事 by MoNoQLoREATOR » 7年前

>>ISLeさん
初耳です!!
私は気になったトピックしか読みませんから見たことなかったですね。
DXライブラリさんが独自の色コードを使っているというわけではなかったのですか。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: マルチスレッドの仕樣について

#20

投稿記事 by softya(ソフト屋) » 7年前

MoNoQLoREATOR さんが書きました:>>ISLeさん
初耳です!!
私は気になったトピックしか読みませんから見たことなかったですね。
DXライブラリさんが独自の色コードを使っているというわけではなかったのですか。
一応リファレンスに
「DXライブラリ置き場 リファレンスページ」
http://homepage2.nifty.com/natupaji/DxL ... .html#R4N5

<<注意!>>
 色コードは画面のカラービット数によって変化しますので、画面のカラービット数が変化するとそれ以前にこの関数で得られた色コードは無効( 別の色を表す数値 )になります。

とだけ書かれています。
あとは他の人も同じである保証もしないって事でしょう。
現時的にはWindowsで使える何らかのカラーコードですがGPUの初期の頃のカオスっぷりを知っているので並び順が同じである保証など無いと思ったほうが安全です。
※ 現実的には、そんなにバリエーションはないでしょうが。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

閉鎖

“C言語何でも質問掲示板” へ戻る