スレッドに複数の引数を渡すには

解決


三日で発狂  2006-01-21 09:14:26  No: 60422

typeof  struct data{
        HWND hWnd;
        UINT message;
        WPARAM wParam;
        LPARAM lParam;
} DATA;
のあとに・・・
どうすりゃいいかわかりません。構造体についてはCの絵本で勉強した程度です。
できれば具体例を挙げてCreatThreadにダイアログのHWND,UINT,WPARAM,LPARAMの渡し方を書いていただくと幸いです。
なぜこのようなことをしているのかというと
チャットプログラムを作っていて切断ボタン(IDC_BUTTON3とします)を
押したときに受信専用スレッドで終了させたいです。
よろしくお願いします。CreatThreadに複数の引数を渡すには構造体を使うことはわかっています。CreatThreadで作っていますのでCreateThreadについてよろしくお願いします。


3日目で発狂  2006-01-21 09:14:58  No: 60423

言い忘れていましたが
SDKで作成しています。


επιστημη  2006-01-21 09:57:58  No: 60424

DATA型の変数を一つ用意し、その中身を埋め、そのポインタを引数に与えればいい。

DATA data;
data.hWnd = ...
...
CreateThread( ... &data ... )

スレッド側では:

DWORD WINAPI service(LPVOID arg) {
  DATA* pdata = (DATA*)arg;
  HWND hWnd = pdata->hWnd;
  ...
}


三日目で発狂  2006-01-21 19:12:01  No: 60425

問題なくコンパイルできたのですが(エラー0、警告0)
なぜかスレッドの方に正しく引数が渡されていないみたいです。
ふと思ったのですが、毎回スレッド側でメインのダイアログのHWNDとかUINTとかを取得しなければいけないですよね?

一応記述したソースを張っておきます。
resource.h
-----------
いろいろ

BOOL CALLBACK DlgWndProc(HWND, UINT, WPARAM, LPARAM);

DWORD WINAPI fnThread1(LPVOID);
void printTextserver(TCHAR *,LPVOID);
void printTextclient(TCHAR *,LPVOID);

        typedef struct data{
    HWND hWnd;
    UINT message;
    WPARAM wParam;
    LPARAM lParam;
        } DATA;

-----------------
main.cpp
--------------------
//ダイアログボックスのウインドウプロシージャ
BOOL CALLBACK DlgWndProc(HWND hWnd,
                                                 UINT message,
                                                 WPARAM wParam,
                                                 LPARAM lParam)
{
///省略
                case IDC_BUTTON2:
                                DATA data;
        data.hWnd = hWnd;
        data.message =message;
        data.wParam = wParam;
        data.lParam = lParam;
                        if (GetWindowTextLength(GetDlgItem(hWnd,IDC_EDIT3)) != 0){
                        hThread1=CreateThread(NULL,0,fnThread1,&data,0,NULL); /* スレッド作成 */
                        
//省略
-------------------
socket.cpp(スレッド)
----------------
DWORD WINAPI fnThread1(LPVOID lpV) {
               
DATA* pdata = (DATA*)lpV;
HWND hWnd = pdata->hWnd;
WPARAM wParam = pdata->wParam;
//省略
 if(LOWORD(wParam) == IDC_BUTTON3){
SetWindowText(GetDlgITem(hWnd,IDC_EDIT4);
}
//省略
}
よろしくお願いします。


επιστημη  2006-01-21 19:40:03  No: 60426

> ふと思ったのですが、毎回スレッド側でメインのダイアログのHWNDとかUINTとかを取得しなければいけないですよね?

スレッドに渡したのはあくまでポインタです。実体ではありません。
そのことから「明らか」でしょう。


三日目で発狂  2006-01-21 19:45:12  No: 60427

ポインタはアドレスを指し示すものだったと思うので
なるほど、毎回取得しなくていいんですね・・

ではどうしたらいいのでしょうか・・
よろしくお願いします。


επιστημη  2006-01-21 19:52:12  No: 60428

>> ふと思ったのですが、毎回スレッド側でメインのダイアログのHWNDとかUINTとかを取得しなければいけないですよね?
>
> スレッドに渡したのはあくまでポインタです。実体ではありません。
> そのことから「明らか」でしょう。

ああ、そういう意味じゃないのか…

本体が受理したメッセージをスレッドに横流しするメカニズムが要るんじゃないか、と訊いているのかな?
メッセージに対する処理をスレッドでやりたいのなら、答は YES。

ITC(Inter Thread Communication)をやらねばなりませんです。
簡単に済ますならEvent待ちを仕込む、少し凝るならProducer-Consumer-Queueあたりかな。


三日で発狂  2006-01-21 20:03:55  No: 60429

ありがとうございます!!
>>本体が受理したメッセージをスレッドに横流しするメカニズムが要るんじゃないか、と訊いているのかな?
まさにそのとおりです。

的確なヒントのような感じがするので提示されたキーワードで
検索してみます。
ITC(Inter Thread Communication)関係のURL提示していただければ幸いです。


επιστημη  2006-01-21 20:15:20  No: 60430

とりあえずの取っ掛かりにここいら↓はどうだろう。
http://www.ops.dti.ne.jp/~allergy/thread/thread.html#event


三日目で発狂  2006-01-21 20:44:12  No: 60431

回答ありがとうございます。
応用するとすれば

冒頭で
        HANDLE  m_hEvent;

        m_hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); // 自動リセットイベント作成
して・・?
WaitForSingleObject(m_hEvent,INFINITE);         // イベント待ち

いまいち理解度が上がりません。CreateEventで自動リセットのイベントを作成し戻り値のハンドルを監視すると言った感じでしょうか?


επιστημη  2006-01-21 20:48:59  No: 60432

ダイアログがメッセージを受けたら

1. スレッドに与えるべきデータを整え
2. Eventを発行する。

スレッドは:

1. WaitForSingleObjectでEventが発生するのを待つ。
2. Eventが発生したら(1)から抜けるので、
    ダイアログが設定してくれたデータを読み、処理する
3. (1)に戻る


三日目で発狂  2006-01-21 21:06:10  No: 60433

すばやいレスありがとうございます。
大変恐縮です。

メインでの処理
typeof  struct data{
    HWND hWnd;
    UINT message;
    WPARAM wParam;
    LPARAM lParam;
} DATA;
DATA data = CreateEvent(NULL,TRUE,FALSE,NULL);

上記で個人的に1,2をやったつもりです。

スレッド側
WaitForSingleObject(data,INFINITE);         // イベント待ち
上記のスレッド側の1から抜ける
ダイアログから設定した処理を読み込む
HWND hWnd = data->hWnd;
WPARAM wParam = data->wParam;

3の1に戻るはどうしたらいいのでしょうか?この場合必要なのでしょうか?SetEventはどういった処理をしているのでしょうか?

質問ばっかりで大変恐縮です。
早いレスをいただいて大変うれしかったです。
すみませんが、今から仕事に出勤しなければならないのでレスは明日になってしまいます。もうしわけございません。

ありがとうございます。


επιστημη  2006-01-21 21:34:19  No: 60434

/*
 * event.cpp: まほろさんの一日
 */

#include <windows.h>
#include <iostream>
#include <string>

struct command {
  std::string what;
};

DWORD WINAPI maid(LPVOID lpV) {
  command* cmd = reinterpret_cast<command*>(lpV);
  std::cout << "御用の折には遠慮なくお申し付けくださいませ\n";
  HANDLE call = OpenEvent(SYNCHRONIZE, TRUE, "call");
  bool shouldwait = true;
  while ( shouldwait ) {
    DWORD result = WaitForSingleObject(call, 5000);
    switch ( result ) {
    case WAIT_OBJECT_0:
      if ( cmd->what == "bye" ) return 0;
      std::cout << "優さん、" << cmd->what << " でございますね!\n";
      break;
    case WAIT_TIMEOUT:
      std::cout << "ヒマだわ…お昼寝しましょ。\n";
      break;
    default:
      shouldwait = false;
    }
  }
  return 0;
}

int main() {
  command cmd;
  HANDLE call = CreateEvent(NULL, FALSE, FALSE, "call");
  HANDLE thread = CreateThread(NULL,0,maid,&cmd,0,NULL);
  while ( true ) {
    std::cin >> cmd.what;
    SetEvent(call); // まほろさんを呼ぶ
    if ( cmd.what == "bye" ) break;
  }
  CloseHandle(bell);
}


επιστημη  2006-01-21 21:38:52  No: 60435

> DATA data = CreateEvent(NULL,TRUE,FALSE,NULL);

なんだそりゃ?

> 3の1に戻るはどうしたらいいのでしょうか?

無限ループ

> この場合必要なのでしょうか?

知らん。あなたが何をしたいかによる。

> SetEventはどういった処理をしているのでしょうか?

WaitForXXXXObjectによるブロック(停止状態)を解く。

# 「まほろさんの一日」を参照。


επιστημη  2006-01-21 21:40:44  No: 60436

ごめん、優さんに修正ミス
×  CloseHandle(bell);
○  CloseHandle(call);


Ban  2006-01-21 22:09:46  No: 60437

> チャットプログラムを作っていて切断ボタン(IDC_BUTTON3とします)を
> 押したときに受信専用スレッドで終了させたいです。

これを読む限り、DATA 構造体をスレッドに常に渡す意味がないような気が。

もしかして、スレッド側でダイアログ宛の全てのメッセージを見ようと
考えていたりしませんか。であれば、根本的に止めた方がよいと思います。

あくまでダイアログのメッセージはダイアログ側で確認し、
ボタンが押された場合にその中からスレッドに通知を送ってあげる。
これだけならば、そもそもスレッドの起動引数にデータを渡す必要性も
薄いかと思います。

この場合、受信専用スレッドでは「切断用イベント」の通知をもらうだけで
切断を要求されていることがわかりますから、
WaitForMultipleObjectsや WSAWaitForMultipleObjects で、
本来の受信と、切断用イベントの両方に対して待機しておき、
待機明けの時点で、終了要求であれば終了処理を、受信であれば受信処理を、
など個別に処理すればよいように思います。

> 3の1に戻るはどうしたらいいのでしょうか?この場合必要なのでしょうか?
> SetEventはどういった処理をしているのでしょうか?

for や whileで。
受信スレッドという名前から想像するありがちな処理では必要なことが
多いと思いますが、必須かと云われれば「受信専用スレッド」の責務次第では。

> メインでの処理
> typeof  struct data{
  --snip--
> } DATA;
>DATA data = CreateEvent(NULL,TRUE,FALSE,NULL);

CreateEventの戻りはイベントのハンドルです。
DATAに代入などできませんし、意味がありません。MSDNを確認してください。

また、DATAを変数として確保する場合、
スレッドの当該処理完了までDATA存続を保証できるスコープにおく必要があり、
スレッド参照中に次のデータで上書きされる可能性があるなら排他制御等も
必要となります。


Ban  2006-01-21 22:19:01  No: 60438

# うは、書き込んで画面更新されたら、まほろたんが。


επιστημη  2006-01-21 22:24:40  No: 60439

# はい、お仕事ないときゃ寝てばかり。ひよこが飛び交っています。


Ban  2006-01-21 22:55:35  No: 60440

# サンプルなので些事ですが、将来の閲覧者のために。

CreateThreadで起こしたスレッドからiostream呼んだらよくないかと。
# Win32onlyで書かにゃならんです>CreateThreadのハンドラ内部

<MSDN>
CreateThread
 -- snip --
C のランタイムライブラリに記録されている関数を使うスレッドは、
CreateThread 関数と ExitThread 関数ではなく、
C のランタイム関数である beginthread 関数と endthread 関数を使うべきです。
この方法に従わないと、ExitThread 関数を呼び出したときにわずかなメモリリークが発生します。
</MSDN>


επιστημη  2006-01-21 23:04:42  No: 60441

ツッコミに感謝。その通りです。
このままほっとくと

  まほろさんがその機能を停止するまで
                        残り390日

なんちて


3日目で発狂  2006-01-23 06:10:33  No: 60442

レスありがとうございます。
根本的に間違っていたみたいで大変申し訳ないです。
これで解決しそうです。

ありがとうございます。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加