お世話になっております.今回は,複数のスレッドを1つのプログラムで扱おうとしています.つぎはぎだらけのプログラムができたのですが,実行後に右上の×ボタンで終了すると,毎回エラーが出てしまいます.以下にプログラムを示しますので,問題点を教えて頂ければと思います.
CDlg::CDlg(CWnd* pParent /*=NULL*/)
: CDialog(CEvaDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CEvaDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pThread = NULL;
m_bThread = FALSE;
}
UINT CEvaDlg::PpsnThreadFunc( LPVOID pParam )
{
((CEvaDlg*)pParam)->PpsnThread();
return 0;
}
void CDlg::OnButtonPpsn()
{
// TODO: Add your control notification handler code here
if ( m_pThread ) return;
// スレッド開始
m_bThread = TRUE;
m_pThread = AfxBeginThread( PpsnThreadFunc, this );//ThreadFunc が実行されすぐ復帰
}
void CEvaDlg::PpsnThread()
{
for(I=0;I<=512 && m_bThread;I++){
//処理
}
AfxEndThread(0); //以前はm_pThread3=NULLとしていてもほぼプログラムは動いていたが,間違っているのですよね?
}
void CDlg::OnButtonShd() //ここでフラグを立てスレッド自身で終了させる
{
if(!m_pThread)return;
m_pThread->SuspendThread();
m_bThread = FALSE;//以前はm_pThread3=NULL;を入れていたのですが,これも間違っているのですよね?
}
以上のようなスレッドを3つ作ったのですが,実行後に×ボタンで終了するとDebug画面には(スレッドの一つを実行し終了した後),
The thread 0xD74 has exited with code 0 (0x0).
The thread 0x470 has exited with code -1073741819 (0xC0000005).
The thread 0xFFC has exited with code -1073741819 (0xC0000005).
というメッセージが表示されます.3つのスレッドは各々のボタンを押した時に開始させるようにしているつもりなのですが,他の2つのスレッドも開始されてしまっているのでしょうか.また,スレッドの開始・終了方法は上に示したプログラムで正しいのでしょうか?宜しくお願い致します.
なんか訊きたいことがいっぱいみたいだけど・・・
コードが断片的なので良くわからんがかなりむちゃくちゃかもしんない。
CDlg と CEvaDlg ってどんな関係?
CDlg の this を CEvaDlg にキャストしたって動くわけがない。
SuspendThread はスレッドを一時保留にするだけで終了させないよ
そもそも何がしたいのか良くわからんので提案の仕様がないなぁ
返信ありがとうございます.CDlgとCEvaDlgは同じものと考えてください.(書き間違えてしまいました.)つまり,両方ともCDlgということです.
「OnButtonPpsnを押すと,スレッドにより処理が繰り返され,OnButtonShdを押すと,m_bThreadがFALSEになり,スレッドが終了する.」というものです.質問したかったのは,スレッドの使い方で,間違いがないかということです.(エラーが出てしまうので,何かが間違っていると思います)
分かりにくい質問をしてしまい,すいません.
コンパイラは何?
VC++6.0 であるとして (新しいコンパイラではエラーになる記述がある)
めんどくさくて高度な問題はとりあえずおいておいて
ワーカースレッドを作って終了させるという一点だけに注目するなら
ワーカースレッドの起動:提示コードで正しい。新しいコンパイラでエラーになるので
AfxBeginThread(&CDlg::PpsnThreadFunc, this) としておこう。
ワーカースレッドの終了:ワーカースレッドが自ら終了するのが最善
UI スレッド側で行う処理は m_bThread=FALSE; だけでいい
ワーカースレッド側では m_bThread を適宜監視して自ら終了
AfxEndThread を明示的に呼び出すよりは単に return するのが良いと思う
というわけで簡単にサンプル書いてみた
m_bThread と逆ロジックにしてあるので注意
void CMtdlgDlg::OnStart() {
if (m_pThread!=0) return;
m_ExitThread=0;
m_pThread=AfxBeginThread(&CMtdlgDlg::ThreadFuncSugar, this);
}
void CMtdlgDlg::OnStop() {
if (m_pThread==0) return;
m_ExitThread=1;
}
UINT CMtdlgDlg::ThreadFuncSugar(void* p) {
return reinterpret_cast<CMtdlgDlg*>(p)->ThreadFunc();
}
UINT CMtdlgDlg::ThreadFunc() {
while (m_ExitThread==0) {
CString text;
GetDlgItemText(IDC_COUNTER, text);
long x=_tcstol(text, 0, 0);
text.Format(_T("%d"), x+1);
SetDlgItemText(IDC_COUNTER, text);
}
return 0;
}
OnStart で二回目を開始できないとか問題点が残っているけど、宿題にするかな。
これ直そうとすると結構めんどくさいんで
返信遅くなってすいません...サンプルまでありがとうございます!!サンプルプログラムは関数の宣言の仕方が分からなくてエラーが消せず,まだ確かめられていないのですが...汗
私の方のプログラムのvoid CEvaDlg::PpsnThread()の中のAfxEndThread(0) のかわりに,return 0を入れたのですが,error C2562: 'PpsnThread' : 'void' function returning a valueというエラーが出てしまいます.tetrapodさんのサンプルとは宣言の仕方が違うのかなと思ったりしたのですが(UINTで始まるステイトメントが一つ多いので),どのように宣言したらいいものでしょうか?私の方ではDlg.hの中で,
static UINT PpsnThreadFunc( LPVOID pParam );// スレッドは外部関数か static メンバしか呼べないため
void PpsnThread(); // スレッドの処理を書く場所
BOOL m_bThread;//続行フラグ
CWinThread* m_pThread;//スレッドのアドレス
としています.あと,VC++6.0を使用しています.宜しくお願いします.
>error C2562: 'PpsnThread' : 'void' function returning a valueというエラーが出てしまいます
とりあえず、ヘルプでC2562を検索する。とか、Googleで検索してみるとか…
後ろに出ているエラーメッセージを翻訳サイト等で翻訳してみるとか…。
戻り値のない関数なのに、return 0で0を返そうとしているのでエラーになっています。
http://msdn.microsoft.com/ja-jp/library/s3w9x78e(VS.80).aspx
VS2005向けのヘルプなのでVC++6.0とは違うかも知れませんが、
AfxBeginThread()で作成するスレッドはUINT型の戻り値を返すようにすべきです。
>The thread 0x470 has exited with code -1073741819 (0xC0000005).
>The thread 0xFFC has exited with code -1073741819 (0xC0000005).
Access Violation起こしているようですが…。
俺のサンプルは、
UI スレッドで何もしなくても、ワーカースレッドで IDC_COUNTER の表示カウンタが増えていく
というもの。IDC_COUNTER は Dialog に貼った Static-Text Control とする。
で、メンバの宣言はほとんどまったく同じだよ。
class CMtdlgDlg : public CDialog { ... // Wizard の出力は省略
private:
CWinThread* m_pThread;
BOOL m_ExitThread; // volatile が必要な場合あり
static UINT ThreadFuncSugar(void* p);
UINT ThreadFunc();
};
コンストラクタで m_pThread 等の初期化する部分も省略
ご自分でも既にわかっている、あるいは、瀬戸っぷさんの指摘にあるとおり
・ワーカースレッド開始関数には static または非メンバ関数しか指定できないので
ThreadFuncSugar で this を再指定する(このことの是非はおいておき)
・ワーカースレッド関数は UINT を返却する約束であるので
ThreadFunc/Sugar は UINT を返すように作るべし
・ワーカースレッドの終了は、スレッド自身の判断で return するよう作るべし
(AfxEndThread を明示的に呼ぶと、ローカル変数のデストラクタが呼ばれない)
・ワーカースレッドを、その外から SuspendThread するのはデッドロックの元なので禁じ手
アクセスバイオレーションは CWnd の制約を守っていないから、とか?
単にバグっているだけとか?いかんせんコードがないのでわからない。
tetrapodさん,瀬戸っぷさん,ご回答ありがとうございます!!
tetrapodさんのサンプルは確信することができました!再度startするのは,まだ分からないのですが...
自分の方のプログラムは関数の宣言とかいろいろ間違っていて,return 0できなかったのかな...(ネットで調べたつぎはぎだらけのプログラムで,細部までは理解できていないのですが汗)
access violationに関しても,修正したいので,まずは,tetrapodさんのスレッドの作り方にならって,プログラムを書き直してみようと思います.それで,エラーが消えるか確かめてみたいと思います.
tetrapodさんのスレッドの作り方で書き直してみたら,目的のプログラムが作れました!
Access violationはまだ起こっているようで,エラーメッセージがプログラムを閉じるたびに出てきます.ただ,プログラムで行う作業は正常に行われているので,今回は解決にしようと思います.エラー箇所の良い特定方法があればいいのですが...もうちょっと見直してみます!
大変助かりました!ありがとうございますm(-_-)m
ツイート | ![]() |