任意のディレクトリ内の画像ファイルを全て読込み、サムネイルを作成するルーチンを
マルチスレッド化しようと思っています。
下記ルーチン前にSetCurrentDirectory()で、任意のディレクトリに移動しています。
シングルスレッドでは問題なく動作するのですが、マルチスレッドではディレクトリに
よってはうまく行かない場合があります。
スレッド内で画像データと情報の構造体(MDIB)を操作しています。
下記★★の箇所で、シングルスレッドではmp_headImageの構造体にデータがきちんとセ
ットされるのですが、マルチスレッドでNGの場合は、データがきちんとセットされませ
ん。
画像数(スレッド数)に制限があるのかとも思い、スレッド数を変えても結果はNGでし
た。
何が問題なのかわからず悩んでいます。
根本的なところで何か間違いをしているのでしょうか?
ちなみにマルチスレッドでのプログラミングは今回が初めてです。
どなたかご教授の程よろしくお願いします。
開発環境
VC++
SDK
Visual Studio 2003, 2008
//-----------------------------------
// シングルスレッドの場合->問題は起きない
//-----------------------------------
・・・
MDIB *p_mdib = mp_headImage;
for( int i=0; i<m_imgcnt; i++ ) {
GetImage( p_mdib );
CreateThumbNail( p_mdib );
if( p_mdib->mp_nxtmdib ) p_mdib = p_mdib->mp_nxtmdib;
}
//サムネイルを画面に展開
ExpandThumb( mp_headImage ); //★★
}
//-------------------------
//マルチスレッド呼出し側
//-------------------------
・・・
//MDIB=画像データと情報を保持する構造体
//mp_headImage = リンクリスト先頭のMDIBへのポインタ
//thread_param = スレッドへのデータ受渡用構造体
//m_imgcnt = 画像ファイル数
MDIB *p_mdib = mp_headImage;
thread_param *p_tp = GetArea0<thread_param>( m_imgcnt ); //動的領域確保
HANDLE *h_thread = GetArea0<HANDLE>( m_imgcnt ); //動的領域確保
//クリティカル・セクション作成
LockThread lth;
//スレッド生成
p_mdib = mp_headImage;
for( int i=0; i<m_imgcnt; i++ ) {
p_tp[i].p_lth = <h;
p_tp[i].p_mdbc = this;
p_tp[i].p_mdib = p_mdib;
h_thread[i] = (HANDLE)_beginthread( MDIBControl::LoadPict, 0, &p_tp[i] );
if( p_mdib->mp_nxtmdib ) p_mdib = p_mdib->mp_nxtmdib; //次のリンク選択
}
//全てのスレッドの終了待ち
::WaitForMultipleObjects( m_imgcnt, h_thread, TRUE, INFINITE );
delete [] p_tp;
delete [] h_thread;
・・・
//サムネイルを画面に展開
ExpandThumb( mp_headImage ); //★★
}
//-------------
//スレッド本体
//------------
void MDIBControl::LoadPict( void *lp ) {
thread_param *p_tp = (thread_param *)lp;
p_tp->p_lth->Lock();//変数保護
p_tp->p_mdbc->GetImage( p_tp->p_mdib );// 画像と画像情報取得
p_tp->p_mdbc->CreateThumbNail( p_tp->p_mdib );// サムネイル作成
p_tp->p_lth->Release();//変数解放
_endthread();//スレッド終了
}
//-------------------------
//スレッド変数保護クラス
//-------------------------
class LockThread {
private:
CRITICAL_SECTION m_cs;
public:
inline LockThread() { ::InitializeCriticalSection( &m_cs ); }
inline virtual ~LockThread() { ::DeleteCriticalSection( &m_cs ); }
inline Lock() { ::EnterCriticalSection( &m_cs ); }
inline Release() { ::LeaveCriticalSection( &m_cs ); }
};
ディレトリによって処理できなかったりというのは、
GetImage( p_tp->p_mdib );// 画像と画像情報取得
CreateThumbNail( p_tp->p_mdib );//
の中でスレッドで処理してはいけないことでもしてるの
ではないですか?
ところで、
元スレッドはすべてのスレッドが処理を終わるまでWaitして
個々のスレッドはLoadPictでLockをかけて、他のスレッドを
止めてますが、
止めちゃうなら複数起動する意味はなんでしょう?
下のコードの終了判定のend = FALSE にブレークポイントいれて試してみたら
::WaitForMultipleObjects( LOOP_CNT, h_thread, TRUE, INFINITE );
で抜けてきた時は最後のスレッド処理が終わってないです。
スレッド内で完了イベントを投げて
::WaitForMultipleObjects( LOOP_CNT, hEvent, TRUE, INFINITE );
で待てばきちんと終わってます。
h_threadはスレッドが動き出した時にシグナルが立つんでしょうか?
私には解りません。
#include stdafx.h
#include <process.h>
#include <stdio.h>
#include <Windows.h>
#define LOOP_CNT 30
using namespace System;
class LockThread {
private:
CRITICAL_SECTION m_cs;
public:
inline LockThread() { ::InitializeCriticalSection( &m_cs ); }
inline virtual ~LockThread() { ::DeleteCriticalSection( &m_cs ); }
inline void Lock() { ::EnterCriticalSection( &m_cs ); }
inline void Release() { ::LeaveCriticalSection( &m_cs ); }
};
struct thread_param {
LockThread* p_lth;
int in ;
int out ;
int no ;
HANDLE* pEvent ;
BOOL* end_flag;
};
int g_no=0 ;
void __stdcall LoadPict( void *lp ) {
thread_param *p_tp = (thread_param *)lp;
p_tp->p_lth->Lock();//変数保護
p_tp->out = p_tp->in ;
p_tp->no = g_no ;
g_no++ ;
*(p_tp->end_flag) = TRUE ;
p_tp->p_lth->Release();//変数解放
SetEvent(*(p_tp->pEvent));
_endthread();//スレッド終了
}
int main(array<System::String ^> ^args)
{
LockThread lth;
thread_param tp[LOOP_CNT];
HANDLE h_thread[LOOP_CNT];
HANDLE hEvent[LOOP_CNT];
BOOL thread_end[LOOP_CNT];
BOOL end = FALSE;
int i ;
for( i=0; i<LOOP_CNT; i++ ) {
thread_end[i] = FALSE ;
}
for( i=0; i<LOOP_CNT; i++ ) {
tp[i].p_lth = <h;
tp[i].in = i;
tp[i].out = -1;
tp[i].end_flag = &thread_end[i];
hEvent[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
tp[i].pEvent = &hEvent[i];
h_thread[i] = (HANDLE)_beginthread(LoadPict, 0, &tp[i]);
}
//全てのスレッドの終了待ち
//::WaitForMultipleObjects( LOOP_CNT, h_thread, TRUE, INFINITE );
::WaitForMultipleObjects( LOOP_CNT, hEvent, TRUE, INFINITE );
while(!end) {
end = TRUE ;
for( i=0; i<LOOP_CNT; i++ ) {
if(!thread_end[i]) {
end = FALSE ;
break ;
}
}
Sleep(10) ;
}
for( int i=0; i<LOOP_CNT; i++ ) {
printf(in:%d out:%d no:%d th:%d\n, tp[i].in,
tp[i].out, tp[i].no, h_thread[i]) ;
}
return 0;
}
上記の処理の場合は
WaitForMultipleObjects( LOOP_CNT, h_thread, TRUE, INFINITE );
の戻り値にWAIT_FAILEDが返っています。
h_threadの配列に同じIDが重複して登録されていましたので
WaitForMultipleObjectsまでに_beginthreadの最初スレッドは終了していて
後半は同じHANDLE値でスレッドが起動して重複となり、
WaitForMultipleObjectsがエラーとなっているようです
質問者様の処理も同じことが起きてデータがセットされなかったのでは
ありませんか?
よく検証しないで書いたので、連投になってしまいました。
ごめんなさい。
solidさん
色々試して頂いてありがとうございます。
>元スレッドはすべてのスレッドが処理を終わるまでWaitして
>個々のスレッドはLoadPictでLockをかけて、他のスレッドを
>止めてますが、
>止めちゃうなら複数起動する意味はなんでしょう?
確かに自分でもそのように感じたのですが、正常に終わるディレクトリの場合は
明らかに処理が早くなっていたので、意味があるのかと思っていました。
>上記の処理の場合は
>WaitForMultipleObjects( LOOP_CNT, h_thread, TRUE, INFINITE );
>の戻り値にWAIT_FAILEDが返っています。
確かにWaitForMultipleObjects()でWAIT_FAILEDが返却されていました。
>h_threadの配列に同じIDが重複して登録されていましたので
>WaitForMultipleObjectsまでに_beginthreadの最初スレッドは終了していて
>後半は同じHANDLE値でスレッドが起動して重複となり、
>WaitForMultipleObjectsがエラーとなっているようです
h_thread[]をダンプして見ましたが、ハンドルの重複はありませんでした。
またイベントを生成して見ましたが、結果はWAIT_FALIEDが返ってくるだけで
変わりませんでした。
-------------------------------------
私自身何か根本的なところで大きな勘違いをしているような気がしています。
そこで基本的な質問なのですが(別スレを立てるべき?)、
スレッド内で使用できるのは、非同期シグナルセーフな関数のみと書かれたものを
見たことがあるのですが、
呼出し側で使用する変数をスレッド内で動的に確保するようなことは、やっては
いけないことなのでしょうか?
そもそも非同期シグナルセーフな関数とは具体的にどのような関数のことを言うので
しょうか?
根本的な間違いのあるなしに関わらず、マルチスレッドの使い方を学習する上で、
何かアドバイスがあれば合わせてお願い致します。
ご教授の程よろしくお願いします。
お世話になります。
デバッグを進めていて新たにわかったことがあります。
当初スレッドの数を減らした場合NGになっていましたが勘違いで、実際はスレッド24個
までなら正常に終了しました。
複数のディレクトリで試してみても正常に動作しました。
スレッドを幾つまでなら並列実行できるのかは、システムに依存するものなのでしょう
か?スレッドに割り当てられる時間で決まってくるものなのでしょうか?
実際にプログラミングするにあたってその都度実行するスレッドの数が違う場合、どの
ようにスレッド数を決定すればよいのでしょうか?またどうプログラミングするのが
よいのでしょうか?
参考となる文献やアドバイスがあれば、よろしくお願いします。
>スレッドを幾つまでなら並列実行できるのかは、システムに依存するものなのでしょう
>か?スレッドに割り当てられる時間で決まってくるものなのでしょうか?
システムとリソース次第…かと思われます。
私の場合、せいぜい7個程度のスレッドしか使ったことありませんが。
WaitForMultipleObjects()で状態をチェックできるブジェクト数は規定されていますね。
MAXIMUM_WAIT_OBJECTSと、MSDNに。
WinNT.hで64と定義されています。
>確かにWaitForMultipleObjects()でWAIT_FAILEDが返却されていました。
GetLastError()でエラーコードが確認できるかと。
>そもそも非同期シグナルセーフな関数とは具体的にどのような関数のことを言うので
>しょうか?
関数内でstatic変数使っているもの…とか、1つしかないリソースを使用するモノでスレッ
ドセーフに作られていないもの…はダメでしょうね。
例えば標準関数のstrtok()は内部でstatic変数を使用しているかと。
# その対策のため、_beginthread()や_endthread()の使用を求めているワケですが。
GetImage()やCreateThumbNail()の実装は不明ですが。
瀬戸っぷさん
ご返事ありがとうございます。
とりあえずワンタイムで使用できるスレッド数24まで行けるとわかったのですが、シス
テムやリソースに依存するとなると、あらかじめ少ない数にしておくとか、スレッド数
としてパラメータを外部から与えるとかしないと、プログラムとして汎用性のないもの
となってしまいますね^^;
市販のプログラムではどのように対応しているのでしょうか?
あまりスマートではありませんが、
とりあえず以下のように対応することで実現はできました。
皆さんの意見をもう少し聞いてから解決にしたいと思います。
#define MAX_THREAD_CNT 24
//===========================
// スレッド呼出し側
//===========================
・・・
//p_mdib←画像先頭
MDIB *p_mdib = mp_headImage;
//クリティカル・セクション作成
LockThread lth;
//スレッド処理残り数
int leftimgcnt = m_imgcnt;
while( leftimgcnt > 0 ) {
//一度に実行できるスレッド数を制限//
int numof_onetimethread = (leftimgcnt<MAX_THREAD_CNT) ? leftimgcnt:
MAX_THREAD_CNT;
leftimgcnt -= MAX_THREAD_CNT;
//スレッド用パラメータ領域確保
thread_param *p_tp = GetArea0<thread_param>( numof_onetimethread );
//スレッド用ハンドル領域確保
HANDLE *h_thread = GetArea0<HANDLE>( numof_onetimethread );
//スレッド生成
for( int i=0; i<numof_onetimethread; i++ ) {
p_tp[i].p_lth = <h;
p_tp[i].p_mdbc = this;
p_tp[i].p_mdib = p_mdib;
h_thread[i] = (HANDLE)_beginthread( MDIBControl::LoadPict, 0, &p_tp
[i] );
if( p_mdib->mp_nxtmdib ) p_mdib = p_mdib->mp_nxtmdib;
}
//スレッド終了待ち
DWORD kk = ::WaitForMultipleObjects( numof_onetimethread, h_thread,
TRUE, INFINITE );
if( kk==WAIT_FAILED ) {
ErrMsg msg(13);
}
//不要となった領域廃棄
delete [] h_thread;
delete [] p_tp;
} //endof while( leftimgcnt )
・・・
//====================
// スレッド本体
//====================
void MDIBControl::LoadPict( void *lp ) {
thread_param *p_tp = (thread_param *)lp;
//----------------------
//変数保護[Fileハンドル等]
//----------------------
p_tp->p_lth->Lock();
//----------------------
// 画像と画像情報取得
//----------------------
p_tp->p_mdbc->GetImage( p_tp->p_mdib );
//----------------------
//変数解放[FILEハンドル等]
//----------------------
p_tp->p_lth->Release();
//-------------------
// サムネイル作成
//-------------------
p_tp->p_mdbc->CreateThumbNail( p_tp->p_mdib );
//----------------------
//スレッド終了
//----------------------
_endthread();
}
「Windows スレッド数 上限」と言うキーワードでググれば
かなりの情報がヒットしますから確認されると良いと思います。
32ビットで一つのプロセスのメモリの上限が2GBで、
このメモリを全て使ったとして2000個程度のスレッドが使えるみたいですが、
実際にはメインのスレッドで大きなメモリを使っていたり、
その他、スレッド側でもメモリを大きく使っていたりすると
その影響で利用できるスレッド数の上限は変わるようです。
詳しくはググッた結果から確認してください。
考えておかないといけない事はスレッドが必ずしも起動できるとは
限らないと言う点です。スレッドが起動できなかった時の振る舞いについて
予め考えて決めておく必要があると思います。
最初のサブスレッドを起動できなかったらどうするのかとか
そういう話ですね。
スレッド数に関しては多くなれば良いという話では無いと思います。
スレッドの生成にかかるコストやOS側のタイムスケジュール管理など
いろいろなコストが絡んだ結果、スレッド数が多くなる事で反って
遅くなるケースも出てくると思います。
また、順番に動かすような事をするのであれば、
スレッドを起こし直すよりもそのスレッドを待機させておいて
そのスレッドに次々に処理を依頼するような形にすると
スレッドを起こすコストもかかりませんし、無駄にリソースを消費しません。
通常のPCのレベルならうまく機能するスレッド数はせいぜい片手で
足りる程度かなと思います。
後は各スレッドにどれだけうまく処理を割り振れるかと言う話では
ないでしょうか。
スレッドに関してはリソースが競合しない内部処理であれば、
スレッド数を多めに設定して手分けすると言う話もあると思いますが、
ディスクI/Oの様に競合が発生する場合にその部分がネックになって
スレッドを複数上げている効果が得られないケースもあると思います。
うまくコントロールすれば、他のスレッドがファイルを読み込んでいる間に
既にファイルを読み込んだスレッドでファイルI/Oのない処理を進めて
と言うように処理のオーバーラップが出来る可能性はあると思いますが。
同時に上げるスレッド数に関してはリソース競合のような部分も考慮の上で
いくつ程度が最適なのかを調整する必要があると思いますので
数に関してはある程度慎重に考えた方が良いと思います。
PATIOさん
ご返事ありがとうございます。
そうですかスレッドは起動しないことを前提に実装すべきなのですね;
元々シングルスレッドで実装していたので、スレッドが起動しなかった
場合、シングル側に切替えるような処理を考えます。
色々貴重なご意見ありがとうございました。
一応疑問点は解消されたので、解決ということにしたいと思います。
皆さんありがとうございました。
「スレッドが起動しなかった場合、シングル側に切替える
というのは良い設計じゃないと思います。
PATIOさんの
「スレッドを起こし直すよりもそのスレッドを待機させておいて
そのスレッドに次々に処理を依頼するような形にすると
スレッドを起こすコストもかかりませんし、無駄にリソースを消費
しません。」
を軽くスルーされたので、
(これが一番大事と思いまして)汚い例ですが
スレッドを5個立ち上げて順番に処理するコードを書いてみました。
#include stdafx.h
#include <process.h>
#include <stdio.h>
#include <Windows.h>
#define LOOP_CNT 30
#define THREAD_CNT 5
using namespace System;
typedef void (*pict_callback)(void*) ;
enum TBL_STATUS {
TBL_INIT,
TBL_PROC,
TBL_END
};
enum THREAD_STATUS {
THREAD_INIT,
THREAD_WAIT,
THREAD_ACT,
THREAD_START,
THREAD_END,
THREAD_STOP
};
class LockThread {
private:
CRITICAL_SECTION m_cs;
public:
inline LockThread() { ::InitializeCriticalSection( &m_cs ); }
inline virtual ~LockThread() { ::DeleteCriticalSection( &m_cs ); }
inline void Lock() { ::EnterCriticalSection( &m_cs ); }
inline void Release() { ::LeaveCriticalSection( &m_cs ); }
};
struct thread_param {
LockThread* p_ltp;
int tbl_status ;
int in ;
int out ;
int no ;
HANDLE h_thread ;
};
class thread_tbl {
int th_status;
LockThread* p_lth;
HANDLE h_threadEv ;
HANDLE* ph_mainEv ;
thread_param* p_tp ;
public:
thread_tbl(LockThread* pl, HANDLE* pEv)
{
p_tp = NULL ;
p_lth = pl;
ph_mainEv = pEv ;
h_threadEv = CreateEvent(NULL, TRUE, FALSE, NULL);
th_status = THREAD_INIT;
}
~thread_tbl() { ::CloseHandle(h_threadEv); }
int GetStatus() { return th_status; }
void Wait()
{
p_lth->Lock();
::ResetEvent(h_threadEv);
th_status = THREAD_WAIT;
p_lth->Release();
::WaitForSingleObject(h_threadEv, INFINITE);
p_lth->Lock();
::SetEvent(*ph_mainEv);
if(th_status == THREAD_ACT) {
th_status = THREAD_START;
}
else
{
th_status = THREAD_STOP;
}
p_lth->Release();
}
BOOL PulseEvent()
{
if(th_status == THREAD_WAIT){
th_status = THREAD_ACT;
::SetEvent(h_threadEv);
return TRUE;
}
else {
return FALSE;
}
}
void End()
{
th_status = THREAD_END;
::PulseEvent(h_threadEv);
}
void SetParm(thread_param* ptp) { p_tp = ptp ; }
thread_param* GetParm() { return p_tp ; }
};
int g_no=0 ;
void __stdcall LoadPict( void *lp ) {
thread_param *p_tp = (thread_param *)lp;
p_tp->p_ltp->Lock();//変数保護
p_tp->out = p_tp->in ;
p_tp->no = g_no ;
g_no++ ;
p_tp->p_ltp->Release();//変数解放
Sleep(50) ;
p_tp->tbl_status = TBL_END ;
}
void __stdcall threadPict( void *lp ) {
thread_tbl *p_tbl = (thread_tbl *)lp;
while(p_tbl->GetStatus() != THREAD_STOP) {
p_tbl->Wait() ;
if(p_tbl->GetStatus() != THREAD_START) {
break ;
}
LoadPict(p_tbl->GetParm()) ;
p_tbl->SetParm(NULL) ;
}
_endthread();//スレッド終了
}
つづきます
つづき
int main(array<System::String ^> ^args)
{
LockThread lth;
LockThread ltp;
thread_tbl* ptt[THREAD_CNT];
thread_param tp[LOOP_CNT];
int nThread = 0;
HANDLE h_thread[THREAD_CNT];
HANDLE h_mainEV ;
int i,j ;
h_mainEV = CreateEvent(NULL, TRUE, FALSE, NULL);
for( i=0; i<LOOP_CNT; i++ ) {
tp[i].p_ltp = <p;
tp[i].tbl_status = TBL_INIT ;
tp[i].in = i;
tp[i].out = -1;
tp[i].h_thread = NULL ;
}
for( i=0; i<THREAD_CNT; i++ ) {
ptt[i] = new thread_tbl(<h, &h_mainEV) ;
h_thread[i] = (HANDLE)_beginthread(threadPict, 0, ptt[i]);
if(h_thread[i] == (HANDLE)1L) {
break ;
}
nThread++ ;
/* スレッドがWaitするまで待つ */
while(ptt[i]->GetStatus() != THREAD_WAIT)
{
Sleep(1) ;
}
}
while(1)
{
/* 処理完了判定 */
for( j=0; j<LOOP_CNT; j++ ) {
if(tp[j].tbl_status != TBL_END)
{
break ;
}
}
if(j==LOOP_CNT)
{
break ;
}
/* 未処理取得 */
for( j=0; j<LOOP_CNT; j++ ) {
if(tp[j].tbl_status == TBL_INIT)
{
tp[j].tbl_status = TBL_PROC ;
break ;
}
}
if(j==LOOP_CNT)
{
Sleep(10) ;
continue ;
}
/* 実行スレッド取得 */
for( i=0; i<nThread; i++ ) {
if(ptt[i]->GetStatus() == THREAD_WAIT)
{
break ;
}
}
if((i!=nThread) && (j != LOOP_CNT))
{
/* スレッドにテーブルを渡して実行 */
tp[j].h_thread = h_thread[i] ;
::ResetEvent(h_mainEV);
ptt[i]->SetParm(&tp[j]) ;
ptt[i]->PulseEvent() ;
::WaitForSingleObject(h_mainEV, INFINITE);
}
Sleep(1) ;
}
for( int i=0; i<LOOP_CNT; i++ ) {
printf(in:%d out:%d no:%d th:%d\n, tp[i].in,
tp[i].out, tp[i].no, tp[i].h_thread) ;
}
for( i=0; i<nThread; i++ ) {
while(ptt[i]->GetStatus() != THREAD_STOP)
{
ptt[i]->End() ;
}
delete ptt[i] ;
ptt[i] = NULL ;
}
::WaitForMultipleObjects( nThread, h_thread, TRUE, INFINITE );
return 0;
}
MSDNにも書いてありますが、_beginthread, _endthread よりも _beginthreadex,
_endthreadex の方が安全です。
http://msdn.microsoft.com/ja-jp/library/kdzttdcb%28VS.80%29.aspx
『_beginthread よりも _beginthreadex を使用した方が安全です。_beginthread によっ
て生成されたスレッドの終了が早すぎると、_beginthread の呼び出し元に返されるハン
ドルが無効になる可能性や、別のスレッドを指す可能性があります。しかし、
_beginthreadex から返されるハンドルは _beginthreadex の呼び出し元で閉じられる必
要があるため、_beginthreadex がエラーを返さなかった場合にはハンドルが有効である
ことが保証されます。』
また、
『_endthread は、スレッド ハンドルを自動的に終了しますが、_endthreadex は自動的
に終了しません。』
つまり、_endthread により、呼び出し元で保持しているスレッドハンドルは無効になり
ます。
_endthreadex の場合は、呼び出し元で明示的に CloseHandle する必要があります。
上記urlの一番下に _beginthreadex のサンプルがあります。
また、他の方も指摘されていますが、そもそも、ひとつのスレッドが終わるまで他のス
レッドをロックするのであれば、スレッド化する必要は全くありません。
スレッドが起動しない事を前提にするのではなくて
スレッドが起動しない事もあるという事を考慮に入れてというつもりでした。
サブスレッドが一つも起動しない場合、起動に失敗した旨を提示して
エラー終了と言うのも一つの方法です。
いずれにせよ、必ず成功する事を前提にしたプログラムは
よろしくないと考えた方が良いと思います。
メインのGUIを生かした状態にしたい。
無反応状態にしたく無いという話ならワーカースレッド化そのものは
意味があると思います。
但し、そのスレッドが終わらないと次のスレッドが動かないのであれば、
サブスレッドは一つだけにして依頼を次々と渡す方法で十分なのでは
ないかと思います。
solidさん、isshiさん、PATIOさん
フォローありがとうございます。
>スレッドが起動しない事を前提にするのではなくて
>スレッドが起動しない事もあるという事を考慮に入れてというつもりでした。
私もそのつもり(起動しないケースがある)で書いたのですが、言葉足らずで、誤解を招い
てしまいました。すいません。
PATIOさんのおっしゃる意図は伝わっております。
>また、他の方も指摘されていますが、そもそも、ひとつのスレッドが終わるまで他のス
>レッドをロックするのであれば、スレッド化する必要は全くありません。
確かにその通りだと思います。
当初、無条件にロックするようにしていたのですが、ソースを見直したところロックする
必要がなさそうだったので、ロックをはずし、現状はスレッド起動でパラで24個走るよう
に変更しました(これがよいのかは別問題ですが)。
今後皆さんの意見を参考にBrushUpして行きたいと思います。
皆さん貴重なご意見ありがとうございました。