マルチスレッドクラス
マルチスレッドクラス
おかげさまで大分完成してきました。
質問に答えてくださった方々ありがとうございます。
基本的な骨格が完成したので、今回は質問というより批評をお願いしたいと思います。
スレッド作成クラス、ミューテックスを作ったので基本的なスレッド操作は
出来るようになったと思います。
残りはイベントクラス、セマフォクラスを作ったら完成予定です。
基本的なコンセプトは、エラーが起きてもデットロックに陥りにくいように
する。プログラムが途中で停止しない。という2点です。
クラスは全てTKネームスペースの中に入れています。
サンプルコードも入れてあるので使い方は分かると思います。
使用してみて気になったことがあったらコメントを下さい。
#前回の後にコメントしようと思ったらもう過去ログになってた。。。
質問に答えてくださった方々ありがとうございます。
基本的な骨格が完成したので、今回は質問というより批評をお願いしたいと思います。
スレッド作成クラス、ミューテックスを作ったので基本的なスレッド操作は
出来るようになったと思います。
残りはイベントクラス、セマフォクラスを作ったら完成予定です。
基本的なコンセプトは、エラーが起きてもデットロックに陥りにくいように
する。プログラムが途中で停止しない。という2点です。
クラスは全てTKネームスペースの中に入れています。
サンプルコードも入れてあるので使い方は分かると思います。
使用してみて気になったことがあったらコメントを下さい。
#前回の後にコメントしようと思ったらもう過去ログになってた。。。
Re:マルチスレッドクラス
突っ込みどころ満載なので、とりあえず上から順番にいきます。
まずは ThreadException クラスから。
なぜ exlicit を付けないのか、なぜ値渡しなのかも読み取れませんでした。
const char*からの変換コンストラクタも explicit なしでは危険ではないですか?
コピーコンストラクタを定義していないのは意図通りでしょうか?(なくても問題はなさそうです)
データメンバ name の初期化時に std::bad_alloc 例外が送出される可能性があることは想定済みでしょうか?
そもそも Thread::throwErrorCode にかなり無理があるように思います。
まずは ThreadException クラスから。
ThreadException(std::exception e):exception("Thread"),name(e.what()){} ThreadException():exception("Thread"){} ThreadException(const char * e) :exception("Thread"),name(e){}コンストラクタですが、std::exception からの変換コンストラクタの存在意義がよくわかりません。
なぜ exlicit を付けないのか、なぜ値渡しなのかも読み取れませんでした。
const char*からの変換コンストラクタも explicit なしでは危険ではないですか?
コピーコンストラクタを定義していないのは意図通りでしょうか?(なくても問題はなさそうです)
データメンバ name の初期化時に std::bad_alloc 例外が送出される可能性があることは想定済みでしょうか?
ThreadException operator=(ThreadException &e){return e;}代入演算子の形式が変です。これを書くなら、
ThreadException& operator=(const ThreadException &e) { std::string t(e.name); name.swap(t); return *this; }とすべきです。ここでも std::bad_alloc 例外が送出される可能性がありますが、これは避けられません。
const char * getName(){return this->name.c_str();}なぜ const がないのでしょうか? これだとコンパイルできないと思います。
そもそも Thread::throwErrorCode にかなり無理があるように思います。
Re:マルチスレッドクラス
Thread クラスですが、コピーコンストラクタやコピー代入演算子でコピーされると、コピーの数だけ CloseHandle が呼ばれますが、大丈夫でしょうか?
Thread::Pack のデータメンバである errorException と errorFlag ですが、参照先の寿命管理は本当に大丈夫ですか?
Thread::run の中で例外を catch しているのはよいのですが、ThreadException のコンストラクタが失敗するとさらに例外が送出され、その場合は破綻してしまいます。
Thread::Pack のデータメンバである errorException と errorFlag ですが、参照先の寿命管理は本当に大丈夫ですか?
Thread::run の中で例外を catch しているのはよいのですが、ThreadException のコンストラクタが失敗するとさらに例外が送出され、その場合は破綻してしまいます。
Re:マルチスレッドクラス
Mutex::create は変です。もう一度見直してください。
また、Mutex クラスもコピーされた場合に破綻します。
Critical クラスもコピー代入演算子で破綻しますが、それ以前に何か違和感があります。
また、Mutex クラスもコピーされた場合に破綻します。
Critical クラスもコピー代入演算子で破綻しますが、それ以前に何か違和感があります。
Re:マルチスレッドクラス
返信遅くなってすみません。
うえから順番に返答します。
まず変換コンストラクタについてですが、現時点ではまだ何も考えていません。
というより完全に忘れてました。
>コピーコンストラクタを定義していないのは意図通りでしょうか?(なくても問題はなさそうです)
意図的です。
>データメンバ name の初期化時に std::bad_alloc 例外が送出される可能性があることは想定済みでしょうか?
一応std::bad_alloc例外については想定済みです。
何とかしてThreadExceptionに子スレッドでどんな例外が投げられたのかを格納したかったので
メンバにnameを作りました。
例外禁止に出来なければ、nameを削除する予定です。
その場合は親スレッドでどんな例外が投げられたのかが知りようがないのが問題ですが。
>そもそも Thread::throwErrorCode にかなり無理があるように思います。
この無理というのは、Thread::throwErrorCodeが例外を投げること自体が無理なのでしょうか?
「無理」という意味が判断できませんでした。
解説をお願いしたいと思います。
Threadクラスについては欲しい機能を追加しただけで他はまだ触っていないです。
コピーコンストラクタなども考える必要があることは分かっています。
>Critical クラスもコピー代入演算子で破綻しますが、それ以前に何か違和感があります。
コピー代入演算子はprivateにいれて定義してなかったように思いますが。
違和感の理由は多分デストラクタで処理を振り分けているからだろうと思います。
このようにした目的は子スレッドでCritical::unlock()を忘れた場合の時のための
安全策のつもりです。
スマートポインタを介したほうがいいのかなと、思ったのですが
どっちの実装にしたほうが良いのか理由が見つからないので、
ここに関してアドバイスが欲しいと思います。
#来週卒業論文提出なのでこんなことをしている場合ではないような。。。
うえから順番に返答します。
まず変換コンストラクタについてですが、現時点ではまだ何も考えていません。
というより完全に忘れてました。
>コピーコンストラクタを定義していないのは意図通りでしょうか?(なくても問題はなさそうです)
意図的です。
>データメンバ name の初期化時に std::bad_alloc 例外が送出される可能性があることは想定済みでしょうか?
一応std::bad_alloc例外については想定済みです。
何とかしてThreadExceptionに子スレッドでどんな例外が投げられたのかを格納したかったので
メンバにnameを作りました。
例外禁止に出来なければ、nameを削除する予定です。
その場合は親スレッドでどんな例外が投げられたのかが知りようがないのが問題ですが。
>そもそも Thread::throwErrorCode にかなり無理があるように思います。
この無理というのは、Thread::throwErrorCodeが例外を投げること自体が無理なのでしょうか?
「無理」という意味が判断できませんでした。
解説をお願いしたいと思います。
Threadクラスについては欲しい機能を追加しただけで他はまだ触っていないです。
コピーコンストラクタなども考える必要があることは分かっています。
>Critical クラスもコピー代入演算子で破綻しますが、それ以前に何か違和感があります。
コピー代入演算子はprivateにいれて定義してなかったように思いますが。
違和感の理由は多分デストラクタで処理を振り分けているからだろうと思います。
このようにした目的は子スレッドでCritical::unlock()を忘れた場合の時のための
安全策のつもりです。
スマートポインタを介したほうがいいのかなと、思ったのですが
どっちの実装にしたほうが良いのか理由が見つからないので、
ここに関してアドバイスが欲しいと思います。
#来週卒業論文提出なのでこんなことをしている場合ではないような。。。
Re:マルチスレッドクラス
面白そうなので、ちょっとだけ参加します。
・ Criticalクラスのデストラクタで objectCounterが0なら objectを
破棄するようになっていますが、(参照カウンターではないので)
他に共有している objectがいない保証になっていませんが、大丈夫ですか?
・ Criticalクラスの使い方で C++なのに lock() / unlock()を対にして
使わせるのはちょっと・・・。
デストラクタで unlock()忘れ対策をとっているのは判りますが、
対にするならコンストラクタとデストラクタを使った方が確実かと。
・ 連続して lock()を2回以上コールできてしまいますが、意図したとおりでしょうか?
(その場合、デストラクタによる unlock()忘れが正しく機能しない可能性が)。
・ Threadクラスの run()のローカル変数 resultは pf->t()内で例外が発生した時は
何の値を返しているのでしょうか?
・ Criticalクラスのデストラクタで objectCounterが0なら objectを
破棄するようになっていますが、(参照カウンターではないので)
他に共有している objectがいない保証になっていませんが、大丈夫ですか?
・ Criticalクラスの使い方で C++なのに lock() / unlock()を対にして
使わせるのはちょっと・・・。
デストラクタで unlock()忘れ対策をとっているのは判りますが、
対にするならコンストラクタとデストラクタを使った方が確実かと。
・ 連続して lock()を2回以上コールできてしまいますが、意図したとおりでしょうか?
(その場合、デストラクタによる unlock()忘れが正しく機能しない可能性が)。
・ Threadクラスの run()のローカル変数 resultは pf->t()内で例外が発生した時は
何の値を返しているのでしょうか?
Re:マルチスレッドクラス
> >データメンバ name の初期化時に std::bad_alloc 例外が送出される可能性があることは想定済みでしょうか?
> 一応std::bad_alloc例外については想定済みです。
thread.hpp の46行目~55行目の2つの catch 節の中で、ThreadException の自動オブジェクトを生成しています。
このコンストラクタが std::bad_alloc 例外を送出した場合、pf->t も pf も delete の機会を失い、run から例外がリークして破綻すると思うのですが...
> 何とかしてThreadExceptionに子スレッドでどんな例外が投げられたのかを格納したかったので
> メンバにnameを作りました。
> 例外禁止に出来なければ、nameを削除する予定です。
> その場合は親スレッドでどんな例外が投げられたのかが知りようがないのが問題ですが。
格納する内容が文字列だけなら、run の引数として与えるオブジェクトの中に char 配列のメンバを入れておき、strncpy 等でコピーするほうが安全です。
> >そもそも Thread::throwErrorCode にかなり無理があるように思います。
> この無理というのは、Thread::throwErrorCodeが例外を投げること自体が無理なのでしょうか?
> 「無理」という意味が判断できませんでした。
> 解説をお願いしたいと思います。
一番無理がありそうなのは、クライアントコードが errorFlag を知る方法がなく、とりあえず Thread::throwErrorCode を呼んでみて、例外が送出されるかどうかを調べるしかない点です(見落としがあるかもしれませんが...)。
それに、Thread::throwErrorCode は、その仕組み上、元の例外が発生したスレッドの状態に関わらず、非同期に呼び出せてしまいます。しかも、errorFlag は volatile 修飾されていないので、どんな振る舞いをするか予想し切れません。
> >Critical クラスもコピー代入演算子で破綻しますが、それ以前に何か違和感があります。
> コピー代入演算子はprivateにいれて定義してなかったように思いますが。
すみません、これは見落としていました。
> 一応std::bad_alloc例外については想定済みです。
thread.hpp の46行目~55行目の2つの catch 節の中で、ThreadException の自動オブジェクトを生成しています。
このコンストラクタが std::bad_alloc 例外を送出した場合、pf->t も pf も delete の機会を失い、run から例外がリークして破綻すると思うのですが...
> 何とかしてThreadExceptionに子スレッドでどんな例外が投げられたのかを格納したかったので
> メンバにnameを作りました。
> 例外禁止に出来なければ、nameを削除する予定です。
> その場合は親スレッドでどんな例外が投げられたのかが知りようがないのが問題ですが。
格納する内容が文字列だけなら、run の引数として与えるオブジェクトの中に char 配列のメンバを入れておき、strncpy 等でコピーするほうが安全です。
> >そもそも Thread::throwErrorCode にかなり無理があるように思います。
> この無理というのは、Thread::throwErrorCodeが例外を投げること自体が無理なのでしょうか?
> 「無理」という意味が判断できませんでした。
> 解説をお願いしたいと思います。
一番無理がありそうなのは、クライアントコードが errorFlag を知る方法がなく、とりあえず Thread::throwErrorCode を呼んでみて、例外が送出されるかどうかを調べるしかない点です(見落としがあるかもしれませんが...)。
それに、Thread::throwErrorCode は、その仕組み上、元の例外が発生したスレッドの状態に関わらず、非同期に呼び出せてしまいます。しかも、errorFlag は volatile 修飾されていないので、どんな振る舞いをするか予想し切れません。
> >Critical クラスもコピー代入演算子で破綻しますが、それ以前に何か違和感があります。
> コピー代入演算子はprivateにいれて定義してなかったように思いますが。
すみません、これは見落としていました。
Re:マルチスレッドクラス
お二人のコメントをいただいて改良版を作ってみました。
コメントいただいた事は大部分は修正できたと思います。
穴があると自分で認識しているのは、
Justy様の
>連続して lock()を2回以上コールできてしまいますが、意図したとおりでしょうか?
の
連続してlock()を呼ぶことが出来ないようにすることを実装するのを忘れていたぐらいです。
今回も批評をお願いしたいとおもいます。
個人でしか使うつもりはありませんが、
実用に耐えるようにしたいと思っています。
#出来ればお二方だけでなくていろんな人からの突込みがほしいかな。
#突っ込まれた分だけ勉強になったので。
コメントいただいた事は大部分は修正できたと思います。
穴があると自分で認識しているのは、
Justy様の
>連続して lock()を2回以上コールできてしまいますが、意図したとおりでしょうか?
の
連続してlock()を呼ぶことが出来ないようにすることを実装するのを忘れていたぐらいです。
今回も批評をお願いしたいとおもいます。
個人でしか使うつもりはありませんが、
実用に耐えるようにしたいと思っています。
#出来ればお二方だけでなくていろんな人からの突込みがほしいかな。
#突っ込まれた分だけ勉強になったので。
Re:マルチスレッドクラス
おっと、新しくなってますね。
まだまだいろいろ問題がありそうですが、実用レベルには
大分近づいてきているような気がします。
・ Threadクラスを生成して、即座に破棄するとメモリーリークします。
必ず join()しなければならないという仕様であるなら、
デストラクタで必要性を調べ、必要なら join()した方がいいと思います。
・ STRING_SIZE
C++でこういう定数マクロはちょっと・・・。
ThreadExceptionクラス内に static const(使えないコンパイラなら enum)で
入れた方が定数がクラスに従属しますし、不用意な(プリプロセッサによる)
置き換えが発生しなくなります。
あ、あと 19という直値があります。多分、STRING_SIZE-1を狙ったものだとは
思いますが。
・ Criticalクラスのコピーコンストラクタって必要なのでしょうか?
Criticalクラスの役割を考えると operator=()は無効にしておきながら、
コピーコンストラクタを実装する意味があまりよくわかりませんでした。
(ひょっとして、bar2の引数に実体を渡す為だけ??)
いっそのこと、コピーコンストラクタもなしにすれば、miniCriticalも不要になって、
Critical/Critical::LockObjectの実装がより簡単になります。
(実体をそのまま持てばいいので、LockObjectへのポインタを保持する必要もなくなるかも・・・)
・ Lockクラスがコピー可能なのは危険なのでは?
・ 前にも書きましたが、Threadクラスの run()のローカル変数 resultは
pf->t()内で例外が発生した時は何の値を返しているのでしょうか?
(不定を返す仕様ならば無視して下さい)
まだまだいろいろ問題がありそうですが、実用レベルには
大分近づいてきているような気がします。
・ Threadクラスを生成して、即座に破棄するとメモリーリークします。
[color=#d0d0ff" face="monospace] {
Thread thread1(std::bind2nd(std::ptr_fun(&bar2), critical));
} //ここでリーク
[/color]
必ず join()しなければならないという仕様であるなら、
デストラクタで必要性を調べ、必要なら join()した方がいいと思います。
・ STRING_SIZE
C++でこういう定数マクロはちょっと・・・。
ThreadExceptionクラス内に static const(使えないコンパイラなら enum)で
入れた方が定数がクラスに従属しますし、不用意な(プリプロセッサによる)
置き換えが発生しなくなります。
あ、あと 19という直値があります。多分、STRING_SIZE-1を狙ったものだとは
思いますが。
・ Criticalクラスのコピーコンストラクタって必要なのでしょうか?
Criticalクラスの役割を考えると operator=()は無効にしておきながら、
コピーコンストラクタを実装する意味があまりよくわかりませんでした。
(ひょっとして、bar2の引数に実体を渡す為だけ??)
いっそのこと、コピーコンストラクタもなしにすれば、miniCriticalも不要になって、
Critical/Critical::LockObjectの実装がより簡単になります。
(実体をそのまま持てばいいので、LockObjectへのポインタを保持する必要もなくなるかも・・・)
・ Lockクラスがコピー可能なのは危険なのでは?
・ 前にも書きましたが、Threadクラスの run()のローカル変数 resultは
pf->t()内で例外が発生した時は何の値を返しているのでしょうか?
(不定を返す仕様ならば無視して下さい)
Re:マルチスレッドクラス
改良版をさらに改良しました。
今回は結構悩みました。
私にはlock()を2回呼ばないようにできなかったので、
その部分を除いては、今までの指摘されたことすべて改良しました。
(2回呼んでも大丈夫みたいですが)
>・ Threadクラスを生成して、即座に破棄するとメモリーリークします。
今回の実装で一番大変だったのがこの部分です。メモリを管理するスレッドを用意して考えました。
>コピーコンストラクタを実装する意味があまりよくわかりませんでした。
>(ひょっとして、bar2の引数に実体を渡す為だけ??)
ここはご指摘の通り実体を渡すためだけです。
>前にも書きましたが、Threadクラスの run()のローカル変数 resultは
>pf->t()内で例外が発生した時は何の値を返しているのでしょうか?
このresultは他の部分に影響を与えてないように思われるので不定を返しても問題ないような
気がしていたのでそのままにしていました。
今回よくわからなくて実装が途中やめになっている部分があります。
beginthreadex()がエラーを返した時の、errno変数の使い方です。
今考えているのはマルチスレッドなのでエラーを返したことをする確認してerrnoを取得
したとしても、間に別のスレッドで別のエラーをが入っているかも知れないので、
どのようにしてbeginthreadex()のエラーを取得すればよいのかということろです。
#なんとか卒論も提出できてこれで卒業がほぼ確定しました。
今回は結構悩みました。
私にはlock()を2回呼ばないようにできなかったので、
その部分を除いては、今までの指摘されたことすべて改良しました。
(2回呼んでも大丈夫みたいですが)
>・ Threadクラスを生成して、即座に破棄するとメモリーリークします。
今回の実装で一番大変だったのがこの部分です。メモリを管理するスレッドを用意して考えました。
>コピーコンストラクタを実装する意味があまりよくわかりませんでした。
>(ひょっとして、bar2の引数に実体を渡す為だけ??)
ここはご指摘の通り実体を渡すためだけです。
>前にも書きましたが、Threadクラスの run()のローカル変数 resultは
>pf->t()内で例外が発生した時は何の値を返しているのでしょうか?
このresultは他の部分に影響を与えてないように思われるので不定を返しても問題ないような
気がしていたのでそのままにしていました。
今回よくわからなくて実装が途中やめになっている部分があります。
beginthreadex()がエラーを返した時の、errno変数の使い方です。
今考えているのはマルチスレッドなのでエラーを返したことをする確認してerrnoを取得
したとしても、間に別のスレッドで別のエラーをが入っているかも知れないので、
どのようにしてbeginthreadex()のエラーを取得すればよいのかということろです。
#なんとか卒論も提出できてこれで卒業がほぼ確定しました。
Re:マルチスレッドクラス
うーん、イベントを持ってきましたか。
なんだかちょっと大げさなクラスになってきましたね(^^;
■ デッドロックするかもしれません
Threadオブジェクトを作ったとき、下記の2つがかち合ってしまい、デッドロックを起こす可能性はありませんか?
・ Threadクラスコンストラクタの WaitForSingleObject(this->first,INFINITE);
・ Threadクラス template関数 setPack()の WaitForSingleObject(*p_t->second,INFINITE);
(自動リセットオブジェクトにすれば、大丈夫そうですが)
■ ハンドルの解放忘れ
Threadクラスは正常に動作すると4つのハンドルを作成しますが、うち1つしか解放していません。
つまり、このクラスを使うたびに3つのハンドルが溜まっていくことになります。
□ const オブジェクト
ThreadExceptionクラスのコンストラクタや、catch節のオブジェクトなど constを
付けておいた方がいいかと。
□ Critical::LockObjectの miniCritical オブジェクト
何に使ってます?
>私にはlock()を2回呼ばないようにできなかったので
とりあえず Criticalクラスの lock()/unlock()を privateに配置して、
Lockクラスを friend指定にすれば、(Lockクラスでしか使われていないので)
現状2回呼ばれる・・・というか lock()/unlock()の呼ばれる回数がおかしくなる
ことは防げますね。
2回コールを防ぐには lock()が呼ばれたらフラグを trueに、unlock()が呼ばれたら falseに
するようにして、フラグが trueなのに lock()がよばれたら・・・でチェックできるのでは
ないでしょうか。
>今回の実装で一番大変だったのがこの部分です。メモリを管理するスレッドを用意して考えました
うーん、この為だけにイベント2つにスレッド1つですか。
ちょっともったいない気もしますね。
どうせ Threadクラスのデストラクタでスレッドの終了待ちをしなければならないのであれば、
Threadクラスに Pack<F>オブジェクトへのポインタを持つちょっと特殊なスマートポインタ(?)を
持たせて、そのデストラクタを利用してメモリを破棄してもいいような。
(ちょっと特殊なスマートポインタになるのは、Threadクラスのコンストラクタの template引数に
よって中身の Pack<F>の Fが変化するからです)
>このresultは他の部分に影響を与えてないように思われるので
直接的に影響はないのですが、コンパイラによっては警告が出たりしますので・・・。
>beginthreadex()がエラーを返した時の、errno変数の使い方です。
たしか、Visual Studioで MTでビルドしているなら、スレッド毎に異なる変数を見に行くはずなので
別のスレッドの値が入ることはなかったかと。
それ以外だとわかりませんが・・・。
なんだかちょっと大げさなクラスになってきましたね(^^;
■ デッドロックするかもしれません
Threadオブジェクトを作ったとき、下記の2つがかち合ってしまい、デッドロックを起こす可能性はありませんか?
・ Threadクラスコンストラクタの WaitForSingleObject(this->first,INFINITE);
・ Threadクラス template関数 setPack()の WaitForSingleObject(*p_t->second,INFINITE);
(自動リセットオブジェクトにすれば、大丈夫そうですが)
■ ハンドルの解放忘れ
Threadクラスは正常に動作すると4つのハンドルを作成しますが、うち1つしか解放していません。
つまり、このクラスを使うたびに3つのハンドルが溜まっていくことになります。
□ const オブジェクト
ThreadExceptionクラスのコンストラクタや、catch節のオブジェクトなど constを
付けておいた方がいいかと。
□ Critical::LockObjectの miniCritical オブジェクト
何に使ってます?
>私にはlock()を2回呼ばないようにできなかったので
とりあえず Criticalクラスの lock()/unlock()を privateに配置して、
Lockクラスを friend指定にすれば、(Lockクラスでしか使われていないので)
現状2回呼ばれる・・・というか lock()/unlock()の呼ばれる回数がおかしくなる
ことは防げますね。
2回コールを防ぐには lock()が呼ばれたらフラグを trueに、unlock()が呼ばれたら falseに
するようにして、フラグが trueなのに lock()がよばれたら・・・でチェックできるのでは
ないでしょうか。
>今回の実装で一番大変だったのがこの部分です。メモリを管理するスレッドを用意して考えました
うーん、この為だけにイベント2つにスレッド1つですか。
ちょっともったいない気もしますね。
どうせ Threadクラスのデストラクタでスレッドの終了待ちをしなければならないのであれば、
Threadクラスに Pack<F>オブジェクトへのポインタを持つちょっと特殊なスマートポインタ(?)を
持たせて、そのデストラクタを利用してメモリを破棄してもいいような。
(ちょっと特殊なスマートポインタになるのは、Threadクラスのコンストラクタの template引数に
よって中身の Pack<F>の Fが変化するからです)
>このresultは他の部分に影響を与えてないように思われるので
直接的に影響はないのですが、コンパイラによっては警告が出たりしますので・・・。
>beginthreadex()がエラーを返した時の、errno変数の使い方です。
たしか、Visual Studioで MTでビルドしているなら、スレッド毎に異なる変数を見に行くはずなので
別のスレッドの値が入ることはなかったかと。
それ以外だとわかりませんが・・・。
Re:マルチスレッドクラス
修正したつもりでしたが、修正できてなかったところをすべて突っ込まれてました。
きちんとコードは確認しておかないと。
>Threadクラスに Pack<F>オブジェクトへのポインタを持つちょっと特殊なスマートポインタ(?)を
>持たせて、そのデストラクタを利用してメモリを破棄してもいいような。
その方法も考えたのですが、実装方法がわからなかったので、上記のようにスレッドで管理しました。
簡単なサンプルコードを提示していただければ助かります。
きちんとコードは確認しておかないと。
>Threadクラスに Pack<F>オブジェクトへのポインタを持つちょっと特殊なスマートポインタ(?)を
>持たせて、そのデストラクタを利用してメモリを破棄してもいいような。
その方法も考えたのですが、実装方法がわからなかったので、上記のようにスレッドで管理しました。
簡単なサンプルコードを提示していただければ助かります。
Re:マルチスレッドクラス
ありがとうございます。
ざっと見てみました、継承を使うことは思いつかなかったです。
他にも色々と手が入っていたようなので、変更点を確認して、さらに完成を目指していきたいと思います。
ざっと見てみました、継承を使うことは思いつかなかったです。
他にも色々と手が入っていたようなので、変更点を確認して、さらに完成を目指していきたいと思います。
Re:マルチスレッドクラス
>他にも色々と手が入っていたようなので
つい気になったところをついでに直してしまいましたが、
ちょっと個人的な意見を。
Threadのデストラクタでスレッドの終了待ちをしていますが、
これは先の私のコードにしても組木紙織さんのコードにしても
リソース(発生した例外情報)の解放がスレッドが終了しないと出来ない、という観点から
必要な処理だとはおもいますが、現実的にはこれはあまりよろしくないように思えます。
たとえば
のようなコードの場合。
デストラクタでスレッドの終了待ちをしてしまうと、bar1スレッドの内容によっては(無限ループとか)
そのまま処理が止まってしまうのでうまくありません。
根本的な事を言ってしまいますと、Threadクラスからスレッド内で発生した例外の状況を知る、
ということは本当に必要なことなのでしょうか。
一見その機能は便利に思えるのですが、ずっと過去にスレッド内で発生した例外が
あるかどうかを取得する程度の意味合いでしかないですし、
(私的には)スレッド内で発生した例外はスレッド内で処理しないとまずいことが多いので、
使いどころが難しい機能となります。
勿論、そのスレッド内で例外の補足を怠った、或いは失敗したのなら、run()内で
それを補足する必要はありますが、その場合は警告を出して即座に
プログラムを止めてしまってもいいたぐいのエラーではないでしょうか。
とまぁ、Threadクラスから例外の状況取得という機能がなくなれば、
TThreadのデストラクタでスレッドの終了待ちが必要なくなって、
先のコードでも Threadオブジェクトは即座に消滅しハンドルも破棄するけど、
bar1は走り続ける、というコードになります。
いかかでしょうか?
それでもやっぱり後から例外の状況を取得したい、ということであれば
アプローチを変えればできるかと思います。
一例を挙げるなら例えば、今はユーザースレッドの指定を関数オブジェクトで行っていますが、
純粋仮想関数 run()や、仮想関数 exception()とかを持ったユーザースレッド専用の
基底クラス ThreadRunを作って、それを継承してユーザが独自のクラスを作り、
そのオブジェクトを Threadクラスに与えることで ThreadRunの run()をスレッドで実行するようにする。
内部で例外を補足したら 仮想関数 exception()を呼び、例外の情報をセットする。
で、外部からの例外の発生状況の取得はその継承したクラスから取得する、
という流れで一応要望は満たせます。
或いはまぁ単純にスレッドに指定した関数の引数に例外情報を格納する構造体への参照とかポインタとか
入れてしまうとか。
つい気になったところをついでに直してしまいましたが、
ちょっと個人的な意見を。
Threadのデストラクタでスレッドの終了待ちをしていますが、
これは先の私のコードにしても組木紙織さんのコードにしても
リソース(発生した例外情報)の解放がスレッドが終了しないと出来ない、という観点から
必要な処理だとはおもいますが、現実的にはこれはあまりよろしくないように思えます。
たとえば
[color=#d0d0ff" face="monospace] {
Thread thread1(std::bind2nd(std::ptr_fun(&bar1), &critical));
}[/color]
のようなコードの場合。
デストラクタでスレッドの終了待ちをしてしまうと、bar1スレッドの内容によっては(無限ループとか)
そのまま処理が止まってしまうのでうまくありません。
根本的な事を言ってしまいますと、Threadクラスからスレッド内で発生した例外の状況を知る、
ということは本当に必要なことなのでしょうか。
一見その機能は便利に思えるのですが、ずっと過去にスレッド内で発生した例外が
あるかどうかを取得する程度の意味合いでしかないですし、
(私的には)スレッド内で発生した例外はスレッド内で処理しないとまずいことが多いので、
使いどころが難しい機能となります。
勿論、そのスレッド内で例外の補足を怠った、或いは失敗したのなら、run()内で
それを補足する必要はありますが、その場合は警告を出して即座に
プログラムを止めてしまってもいいたぐいのエラーではないでしょうか。
とまぁ、Threadクラスから例外の状況取得という機能がなくなれば、
TThreadのデストラクタでスレッドの終了待ちが必要なくなって、
先のコードでも Threadオブジェクトは即座に消滅しハンドルも破棄するけど、
bar1は走り続ける、というコードになります。
いかかでしょうか?
それでもやっぱり後から例外の状況を取得したい、ということであれば
アプローチを変えればできるかと思います。
一例を挙げるなら例えば、今はユーザースレッドの指定を関数オブジェクトで行っていますが、
純粋仮想関数 run()や、仮想関数 exception()とかを持ったユーザースレッド専用の
基底クラス ThreadRunを作って、それを継承してユーザが独自のクラスを作り、
そのオブジェクトを Threadクラスに与えることで ThreadRunの run()をスレッドで実行するようにする。
内部で例外を補足したら 仮想関数 exception()を呼び、例外の情報をセットする。
で、外部からの例外の発生状況の取得はその継承したクラスから取得する、
という流れで一応要望は満たせます。
或いはまぁ単純にスレッドに指定した関数の引数に例外情報を格納する構造体への参照とかポインタとか
入れてしまうとか。
Re:マルチスレッドクラス
>Threadのデストラクタでスレッドの終了待ちをしていますが、...
そういえば子のスレッドを途中で破棄すると、子スレッド中でautoで生成したオブジェクトの
デストラクタが呼ばれることはないんですよね。
ということはThreadのデストラクタで子スレッドの終了待ちをしたほうが安全なのかな。
無限ループだけは気をつけないといけなくなりますが。
ただThreadのデストラクタが子スレッドの終了を待つ時間がもったいないなと思ったので
子スレッドを途中で破棄したかったということがあります。
>勿論、そのスレッド内で例外の補足を怠った、或いは失敗したのなら、run()内で
>それを補足する必要はありますが、その場合は警告を出して即座に
>プログラムを止めてしまってもいいたぐいのエラーではないでしょうか。
そのような考え方もありますか。
一番最初にあげたコンセプトからは少し外れるような気もしますが、プログラム作成者にとっては
その方がエラーがわかりやすくなりますね。
ユーザーに選択権を与えるといった方法で考えていきます。
#大幅な組み直しになりそうな予感。
そういえば子のスレッドを途中で破棄すると、子スレッド中でautoで生成したオブジェクトの
デストラクタが呼ばれることはないんですよね。
ということはThreadのデストラクタで子スレッドの終了待ちをしたほうが安全なのかな。
無限ループだけは気をつけないといけなくなりますが。
ただThreadのデストラクタが子スレッドの終了を待つ時間がもったいないなと思ったので
子スレッドを途中で破棄したかったということがあります。
>勿論、そのスレッド内で例外の補足を怠った、或いは失敗したのなら、run()内で
>それを補足する必要はありますが、その場合は警告を出して即座に
>プログラムを止めてしまってもいいたぐいのエラーではないでしょうか。
そのような考え方もありますか。
一番最初にあげたコンセプトからは少し外れるような気もしますが、プログラム作成者にとっては
その方がエラーがわかりやすくなりますね。
ユーザーに選択権を与えるといった方法で考えていきます。
#大幅な組み直しになりそうな予感。
Re:マルチスレッドクラス
>ということはThreadのデストラクタで子スレッドの終了待ちをしたほうが安全なのかな。
>無限ループだけは気をつけないといけなくなりますが。
うーん、大抵の場合終了待ちした方が安全ですね。
ただ、明示的に join()すればいいだけの話なので、
デストラクタでした方がいいか、と言われると先の例のようなケースもあるので
そうでもないような。
まぁ結局は Threadのインスタンスの生存期間 >= スレッドの生存期間で
いいのかどうか、でしょう。
>そういえば子のスレッドを途中で破棄すると、子スレッド中でautoで生成したオブジェクトの
>デストラクタが呼ばれることはないんですよね。
あー、強制的に破棄すると、環境依存ですが呼ばれないこともありますね。
明示的にそうするメソッドがあってもいいとは思いますが、デストラクタで
キャンセルしてしまうのはまずそうです。
スレッド内に(ユーザーフラグによる)キャンセルポイントを作って
キャンセル指示を出した後、join()で待つ、が無難でしょうか。
>>その場合は警告を出して即座に
>>プログラムを止めてしまってもいいたぐいのエラーではないでしょうか。
>そのような考え方もありますか。
次の C++0xにもスレッドクラスが標準ライブラリに入ることになるらしいのですが、
こちらはスレッド内で例外の補足に失敗すると std::terminate()をコールするみたいです。
(30.2.1.2 thread constructors 5)
ttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2521.pdf
>#大幅な組み直しになりそうな予感。
がんばってくださ~い。
>無限ループだけは気をつけないといけなくなりますが。
うーん、大抵の場合終了待ちした方が安全ですね。
ただ、明示的に join()すればいいだけの話なので、
デストラクタでした方がいいか、と言われると先の例のようなケースもあるので
そうでもないような。
まぁ結局は Threadのインスタンスの生存期間 >= スレッドの生存期間で
いいのかどうか、でしょう。
>そういえば子のスレッドを途中で破棄すると、子スレッド中でautoで生成したオブジェクトの
>デストラクタが呼ばれることはないんですよね。
あー、強制的に破棄すると、環境依存ですが呼ばれないこともありますね。
明示的にそうするメソッドがあってもいいとは思いますが、デストラクタで
キャンセルしてしまうのはまずそうです。
スレッド内に(ユーザーフラグによる)キャンセルポイントを作って
キャンセル指示を出した後、join()で待つ、が無難でしょうか。
>>その場合は警告を出して即座に
>>プログラムを止めてしまってもいいたぐいのエラーではないでしょうか。
>そのような考え方もありますか。
次の C++0xにもスレッドクラスが標準ライブラリに入ることになるらしいのですが、
こちらはスレッド内で例外の補足に失敗すると std::terminate()をコールするみたいです。
(30.2.1.2 thread constructors 5)
ttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2521.pdf
>#大幅な組み直しになりそうな予感。
がんばってくださ~い。
Re:マルチスレッドクラス
色々とがんばって組み直してみました。
継承を使ったりテンプレート使ったりで難しい。。
まだ全部完成していませんが、全体的な構成の仕方についてコメントが欲しいと思います。
継承を使ったりテンプレート使ったりで難しい。。
まだ全部完成していませんが、全体的な構成の仕方についてコメントが欲しいと思います。
Re:マルチスレッドクラス
なるほど、こうなりましたか。
>全体的な構成の仕方についてコメント
スレッドを使うのに、実行クラスを作って継承して、くらいまではいいのですが、
ThreadRunHolderにそのクラスの型とデストラクタのポリシーを指定して、
更にそれを Threadのテンプレート引数に、って複雑すぎやしませんか?
前のようなお手軽感がなくてなってしまったような気がします。
・ デストラクタのポリシークラス
ThreadNotBreak/ThreadCanBreakはコンストラクタで処理をしていますが、
これならそれぞれのクラスに同じ関数名を持つ静的関数を用意した方が、
いいと思います。
実質的には変わらないのですが、Threadのデストラクタでオブジェクトを生成して
コールするより、普通に関数コールした方がわかりやすいかな、と。
・ Thread::join()
キャンセル指示はイベントとかOSの仕組みを利用した方が安全だと思います。
・ ThreadRun/My2
Threadクラスでの使われ方で、My2のコンストラクタで volatile int*という引数を
強制されてしまっています。
多少制限があるのは仕方がないのですが、さすがに My2クラスの自由度が
固定されてしまうというのはどうでしょう。
・ このサンプル
main()の threadオブジェクトは作られて即座に破棄されます。
その為デストラクタで this->endFlagが trueになり、そのままスレッドの終了待ちを
行うのですが、この段階では Threadクラスの静的関数 run()が実行されていない
可能性があり、その場合 run()が後からよばれ this->endFlagを falseに初期化して
してから、My2の run()が実行されます。
その場合、スレッドがいつまで経っても終了しなくなります。
Threadのコンストラクタでは、ちゃんと静的関数 run()が実行されたことを
確認した方がいいと思います。
>全体的な構成の仕方についてコメント
スレッドを使うのに、実行クラスを作って継承して、くらいまではいいのですが、
ThreadRunHolderにそのクラスの型とデストラクタのポリシーを指定して、
更にそれを Threadのテンプレート引数に、って複雑すぎやしませんか?
前のようなお手軽感がなくてなってしまったような気がします。
・ デストラクタのポリシークラス
ThreadNotBreak/ThreadCanBreakはコンストラクタで処理をしていますが、
これならそれぞれのクラスに同じ関数名を持つ静的関数を用意した方が、
いいと思います。
実質的には変わらないのですが、Threadのデストラクタでオブジェクトを生成して
コールするより、普通に関数コールした方がわかりやすいかな、と。
・ Thread::join()
キャンセル指示はイベントとかOSの仕組みを利用した方が安全だと思います。
・ ThreadRun/My2
Threadクラスでの使われ方で、My2のコンストラクタで volatile int*という引数を
強制されてしまっています。
多少制限があるのは仕方がないのですが、さすがに My2クラスの自由度が
固定されてしまうというのはどうでしょう。
・ このサンプル
main()の threadオブジェクトは作られて即座に破棄されます。
その為デストラクタで this->endFlagが trueになり、そのままスレッドの終了待ちを
行うのですが、この段階では Threadクラスの静的関数 run()が実行されていない
可能性があり、その場合 run()が後からよばれ this->endFlagを falseに初期化して
してから、My2の run()が実行されます。
その場合、スレッドがいつまで経っても終了しなくなります。
Threadのコンストラクタでは、ちゃんと静的関数 run()が実行されたことを
確認した方がいいと思います。
Re:マルチスレッドクラス
>前のようなお手軽感がなくてなってしまったような気がします。
やっぱり複雑でしたか。
今回は前回より簡単に使えるようにしてみました。
>Threadクラスでの使われ方で、My2のコンストラクタで volatile int*という引数を 強制されてしまっています。
これは子スレッドで無限ループを作ってデットロックに陥らないようにするために必要な
ことなのでどうしようもないと考えています。
>Thread::join()
>キャンセル指示はイベントとかOSの仕組みを利用した方が安全だと思います。
wait関数はOSの仕組みではないのですか?
どのような意図かよく理解できないので解説が欲しいと思います。
スレッドを途中で破棄するときにCloseHandle()を使わないようにとMSDNにあったので
どの関数を使って破棄をすればよいのかわからないのが今の気になっているところです。
ほかはすべて実装したつもりです。
やっぱり複雑でしたか。
今回は前回より簡単に使えるようにしてみました。
>Threadクラスでの使われ方で、My2のコンストラクタで volatile int*という引数を 強制されてしまっています。
これは子スレッドで無限ループを作ってデットロックに陥らないようにするために必要な
ことなのでどうしようもないと考えています。
>Thread::join()
>キャンセル指示はイベントとかOSの仕組みを利用した方が安全だと思います。
wait関数はOSの仕組みではないのですか?
どのような意図かよく理解できないので解説が欲しいと思います。
スレッドを途中で破棄するときにCloseHandle()を使わないようにとMSDNにあったので
どの関数を使って破棄をすればよいのかわからないのが今の気になっているところです。
ほかはすべて実装したつもりです。
Re:マルチスレッドクラス
>>>Threadクラスでの使われ方で、My2のコンストラクタで
>>>volatile int*という引数を 強制されてしまっています。
>これは子スレッドで無限ループを作ってデットロックに陥らないようにするために必要
>なことなのでどうしようもないと考えています。
それはキャンセル指示の為のフラグを Threadクラスと実行しているスレッドと
共有するためだと思いますが、回避する方法はあります(後述)。
>>Thread::join()
>>キャンセル指示はイベントとかOSの仕組みを利用した方が安全だと思います。
>wait関数はOSの仕組みではないのですか?
wait関数というのは WaitForSingleObjectのことですか?
たしかに OSの仕組みですが、これは同期待ちの方です。
キャンセル指示自体は endFlagで行っているので、volatileが付いているとはいえ
これをイベント(CreateEvent/SetEvent)で通知した方が同期もとれていいかなぁ、
と思っただけです。
今回のケースでは実際どっちでもいいのかもしれませんが。
で、先の回避の方法ですが。
単純に一方通行の信号(Threadクラスから実行スレッドへのキャンセル)なので、
TLS(Thread Loacl Storage)を使ってイベントハンドルなどを保持し、
参照カウンタでデータの破棄を行うようにすれば一応できます。
サンプルを添付したので、それを見て頂ければ判るかと思います。
>スレッドを途中で破棄するときにCloseHandle()を使わないようにとMSDNに
それは _beginthreadの話では?
_beginthreadexなら普通に CloseHandle()でいいと思います。
※添付のサンプルは全く完全ではなくおかしなところも多々あるので、あくまで参考程度に(^~
# 追記(19:59) 露骨にダメなところを修正
# 追記(23:06) やっぱりまだおかしかったので、再修正。
ついでに例外補足失敗時の挙動を選べるよう修正。
>>>volatile int*という引数を 強制されてしまっています。
>これは子スレッドで無限ループを作ってデットロックに陥らないようにするために必要
>なことなのでどうしようもないと考えています。
それはキャンセル指示の為のフラグを Threadクラスと実行しているスレッドと
共有するためだと思いますが、回避する方法はあります(後述)。
>>Thread::join()
>>キャンセル指示はイベントとかOSの仕組みを利用した方が安全だと思います。
>wait関数はOSの仕組みではないのですか?
wait関数というのは WaitForSingleObjectのことですか?
たしかに OSの仕組みですが、これは同期待ちの方です。
キャンセル指示自体は endFlagで行っているので、volatileが付いているとはいえ
これをイベント(CreateEvent/SetEvent)で通知した方が同期もとれていいかなぁ、
と思っただけです。
今回のケースでは実際どっちでもいいのかもしれませんが。
で、先の回避の方法ですが。
単純に一方通行の信号(Threadクラスから実行スレッドへのキャンセル)なので、
TLS(Thread Loacl Storage)を使ってイベントハンドルなどを保持し、
参照カウンタでデータの破棄を行うようにすれば一応できます。
サンプルを添付したので、それを見て頂ければ判るかと思います。
>スレッドを途中で破棄するときにCloseHandle()を使わないようにとMSDNに
それは _beginthreadの話では?
_beginthreadexなら普通に CloseHandle()でいいと思います。
※添付のサンプルは全く完全ではなくおかしなところも多々あるので、あくまで参考程度に(^~
# 追記(19:59) 露骨にダメなところを修正
# 追記(23:06) やっぱりまだおかしかったので、再修正。
ついでに例外補足失敗時の挙動を選べるよう修正。
Re:マルチスレッドクラス
さて、新しい方の Threadクラスですが。
・ Threadの特殊化
Threadクラスが特殊化されたものがデフォになっていますが、
結構妙な使い方です。
してみてはどうでしょうか。
・ 静的関数 run
これも NullTypeで特殊化されていますが、内容が1カ所くらいしか違わないのであれば
のようなクラスを用意して、run<F>の Fが NullTypeだったら、caller_run_voidを、
それ以外なら caller_run_otherの runを使ってコールするようにして
まとめてしまった方がメンテが楽です。
・ 破棄
破棄の時の挙動を My2などの継承先にもってきていますが、
どうせならポリシークラスにしてしまい、テンプレート引数でしていさせた方が
間違いがない、かと思います。
・ 関数
以前は普通の関数もスレッドに指定できたと記憶していますが、
以後は関数タイプのものはなし、ということでしょうか?
・ Threadの特殊化
Threadクラスが特殊化されたものがデフォになっていますが、
結構妙な使い方です。
[color=#d0d0ff" face="monospace] template<typename Run>
class Thread<ThreadRunHolder<Run> >
{
...
};
[/color]
1パターンしか特殊化しないのであれば、普通に Threadテンプレートにしてみてはどうでしょうか。
・ 静的関数 run
これも NullTypeで特殊化されていますが、内容が1カ所くらいしか違わないのであれば
[color=#d0d0ff" face="monospace] struct caller_run_void
{
template <typename T>
static void run(ValueType &r, T) { r.run(); }
};
struct caller_run_other
{
template <typename T>
static void run(ValueType &r, T v) { r.run(v); }
};[/color]
のようなクラスを用意して、run<F>の Fが NullTypeだったら、caller_run_voidを、
それ以外なら caller_run_otherの runを使ってコールするようにして
まとめてしまった方がメンテが楽です。
[color=#d0d0ff" face="monospace] template <typename T, typename U>
struct IsSameType { enum { Result = false }; };
template <typename T>
struct IsSameType<T, T> { enum { Result = true }; };
template <bool flag, typename T, typename U>
struct SelectType { typedef T Type; };
template <typename T, typename U>
struct SelectType<false, T, U> { typedef U Type; };
typedef SelectType<IsSameType<F, NullType>::Result, caller_run_void, caller_run_other>::Type Caller;
Caller::run(run, *pf.t);[/color]
・ 破棄
破棄の時の挙動を My2などの継承先にもってきていますが、
どうせならポリシークラスにしてしまい、テンプレート引数でしていさせた方が
間違いがない、かと思います。
・ 関数
以前は普通の関数もスレッドに指定できたと記憶していますが、
以後は関数タイプのものはなし、ということでしょうか?
Re:マルチスレッドクラス
サンプル見てみました。色々と複雑なことをしているようで、理解するのに時間がかかりそうです。
実装するのにはさらに時間がかかりそう。
> 以前は普通の関数もスレッドに指定できたと記憶していますが、
>以後は関数タイプのものはなし、ということでしょうか?
関数タイプのものは、継承を使ったクラスタイプよりユーザーが簡単に作れると思いますが、
関数タイプを実装していなかったのは、実装する頭の余裕がなかったというだけです。
クラスタイプのめどがついたら関数タイプを実装しようとかんがえてはいます。
時々ポリシークラスという言葉を使っていますが、この構造って一種のStrategyパターンですよね。
実行時の変更ができなくて、コンパイル時にすべてが決まってしまう。
とちょっと気になりました。
#最初はこんなに複雑になると予想はしてなかったです。
実装するのにはさらに時間がかかりそう。
> 以前は普通の関数もスレッドに指定できたと記憶していますが、
>以後は関数タイプのものはなし、ということでしょうか?
関数タイプのものは、継承を使ったクラスタイプよりユーザーが簡単に作れると思いますが、
関数タイプを実装していなかったのは、実装する頭の余裕がなかったというだけです。
クラスタイプのめどがついたら関数タイプを実装しようとかんがえてはいます。
時々ポリシークラスという言葉を使っていますが、この構造って一種のStrategyパターンですよね。
実行時の変更ができなくて、コンパイル時にすべてが決まってしまう。
とちょっと気になりました。
#最初はこんなに複雑になると予想はしてなかったです。
Re:マルチスレッドクラス
しばらく見ないうちに、追いきれなくなりましたが...
> 時々ポリシークラスという言葉を使っていますが、この構造って一種のStrategyパターンですよね。
その認識であっていると思いますよ。
「Modern C++ Design」にも、ポリシーが一種の Strategy パターンであることが記されていました。
> 実行時の変更ができなくて、コンパイル時にすべてが決まってしまう。
この例からも分かるように、テンプレートは静的なポリモーフィズムを実現するためのツールとして使えます。
実は、これが Java や C# には真似ができない、C++ の最も強力な機能だったりします。
> 時々ポリシークラスという言葉を使っていますが、この構造って一種のStrategyパターンですよね。
その認識であっていると思いますよ。
「Modern C++ Design」にも、ポリシーが一種の Strategy パターンであることが記されていました。
> 実行時の変更ができなくて、コンパイル時にすべてが決まってしまう。
この例からも分かるように、テンプレートは静的なポリモーフィズムを実現するためのツールとして使えます。
実は、これが Java や C# には真似ができない、C++ の最も強力な機能だったりします。
Re:マルチスレッドクラス
>クラスタイプのめどがついたら関数タイプを実装しようとかんがえてはいます。
なるほど。
>実行時の変更ができなくて、コンパイル時にすべてが決まってしまう。
たしかにそうですね。
しかしそれがメリットでもありますので、上手に使えばその方がいいこともあります。
>#最初はこんなに複雑になると予想はしてなかったです。
いろいろな機能を付けようとすると、多少はそうなりますよね。
スレッドは意外なところに落とし穴がありますし。
世の中にはいろんなところに C++で実装したスレッドライブラリがあるので、
いろいろ見てみるといいかもしれません。
CodeProject: A C++ Cross-Platform Thread Class. Free source code and programming help
ttp://www.codeproject.com/KB/threads/thread_class.aspx
CodeProject: Encapsulating Win32 threads in C++. Free source code and programming help
ttp://www.codeproject.com/KB/threads/thread_win32.aspx
CodeProject: A C++ Thread Class. Free source code and programming help
ttp://www.codeproject.com/KB/threads/ThreadClass.aspx
CodeProject: Creating a C++ Thread Class. Free source code and programming help
ttp://www.codeproject.com/KB/threads/threadobject.aspx
これの翻訳が↓
CodeZine:C++でのスレッドクラスの作成(オブジェクト指向)
ttp://codezine.jp/a/article/aid/1977.aspx?p=1
CodeGuru: Template Thread Library
ttp://www.codeguru.com/cpp/w-p/system/threading/article.php/c5687/
CodeGuru: A Generic C++ Thread Class
ttp://www.codeguru.com/cpp/misc/misc/threadsprocesses/article.php/c3793/
Chapter 15. Boost.Thread
ttp://www.boost.org
ttp://www.boost.org/doc/html/thread.html
Class Poco::Thread
ttp://pocoproject.org/
ttp://pocoproject.org/poco/docs/Poco.Thread.html
なるほど。
>実行時の変更ができなくて、コンパイル時にすべてが決まってしまう。
たしかにそうですね。
しかしそれがメリットでもありますので、上手に使えばその方がいいこともあります。
>#最初はこんなに複雑になると予想はしてなかったです。
いろいろな機能を付けようとすると、多少はそうなりますよね。
スレッドは意外なところに落とし穴がありますし。
世の中にはいろんなところに C++で実装したスレッドライブラリがあるので、
いろいろ見てみるといいかもしれません。
CodeProject: A C++ Cross-Platform Thread Class. Free source code and programming help
ttp://www.codeproject.com/KB/threads/thread_class.aspx
CodeProject: Encapsulating Win32 threads in C++. Free source code and programming help
ttp://www.codeproject.com/KB/threads/thread_win32.aspx
CodeProject: A C++ Thread Class. Free source code and programming help
ttp://www.codeproject.com/KB/threads/ThreadClass.aspx
CodeProject: Creating a C++ Thread Class. Free source code and programming help
ttp://www.codeproject.com/KB/threads/threadobject.aspx
これの翻訳が↓
CodeZine:C++でのスレッドクラスの作成(オブジェクト指向)
ttp://codezine.jp/a/article/aid/1977.aspx?p=1
CodeGuru: Template Thread Library
ttp://www.codeguru.com/cpp/w-p/system/threading/article.php/c5687/
CodeGuru: A Generic C++ Thread Class
ttp://www.codeguru.com/cpp/misc/misc/threadsprocesses/article.php/c3793/
Chapter 15. Boost.Thread
ttp://www.boost.org
ttp://www.boost.org/doc/html/thread.html
Class Poco::Thread
ttp://pocoproject.org/
ttp://pocoproject.org/poco/docs/Poco.Thread.html
Re:マルチスレッドクラス
Justy様のDate: 2008/02/23(土) 19:13に添付されたコードで一部不明な部分があるので、
質問させてください。
zthread.cppの中にある以下のコードです。
void ThreadDetail::BaseParam::Wait()関数が複数回呼ばれる可能性がないようにも思えるのにかかわらず、
if(started)return;
と、複数回呼ばれる可能性を考慮しているようなので、なぜ複数回呼ばれる可能性を考慮する必要が
あるのか?
ということです。
この関数は親スレッドで使用され、子スレッドの準備が終わるまで、親スレッドを待機させるという目的
で使われています。
質問させてください。
zthread.cppの中にある以下のコードです。
void ThreadDetail::BaseParam::Wait()関数が複数回呼ばれる可能性がないようにも思えるのにかかわらず、
if(started)return;
と、複数回呼ばれる可能性を考慮しているようなので、なぜ複数回呼ばれる可能性を考慮する必要が
あるのか?
ということです。
この関数は親スレッドで使用され、子スレッドの準備が終わるまで、親スレッドを待機させるという目的
で使われています。
void ThreadDetail::BaseParam::Wait() { if(started) return; DWORD result = WaitForSingleObject(start_handle, INFINITE); if(result == WAIT_OBJECT_0) { CloseHandle(start_handle); start_handle = 0; started = true; } else throw ThreadLogicException(); }
Re:マルチスレッドクラス
>と、複数回呼ばれる可能性を考慮しているようなので、
>なぜ複数回呼ばれる可能性を考慮する必要があるのか?
特にないですね。
使われ方からすれば、複数回呼ばれる可能性はありません。
ただ、BaseParamクラス"単体"で見た時に、一度でも trueを返した後、
呼ばれるとまずいから、そうしたのだと思います(うろ覚え)。
>なぜ複数回呼ばれる可能性を考慮する必要があるのか?
特にないですね。
使われ方からすれば、複数回呼ばれる可能性はありません。
ただ、BaseParamクラス"単体"で見た時に、一度でも trueを返した後、
呼ばれるとまずいから、そうしたのだと思います(うろ覚え)。
Re:マルチスレッドクラス
回答ありがとうございます。
>複数回呼ばれる可能性はない。
ということで私のコードはそれを前提に構築していこうと思います。
ただ、Justy様のサンプルコードは何重にも安全策を施しているようにみえる部分があるので、
安全策を一つだけにせず、何重にも重ねて作っていく必要があるのかなと感じています。
>複数回呼ばれる可能性はない。
ということで私のコードはそれを前提に構築していこうと思います。
ただ、Justy様のサンプルコードは何重にも安全策を施しているようにみえる部分があるので、
安全策を一つだけにせず、何重にも重ねて作っていく必要があるのかなと感じています。
Re:マルチスレッドクラス
>安全策を一つだけにせず、何重にも重ねて作っていく必要があるのかなと感じています
そうですね。
作ってるときは必要ないと思っても他人が使う場合や設計が変更になったりした時に、
そのクラスや関数の設計コンセプトに反した使われ方をする可能性があったりします。
それにあり得ないと思っていても起きてしまうのがプログラムですしね。
そうですね。
作ってるときは必要ないと思っても他人が使う場合や設計が変更になったりした時に、
そのクラスや関数の設計コンセプトに反した使われ方をする可能性があったりします。
それにあり得ないと思っていても起きてしまうのがプログラムですしね。