スレッドプログラミングについて

[戻る]
okuyama 2003/02/05(水) 11:42:54
奥山と申します。

#他のBBSでも書いたのですが、レスがないためこちらでまた質問させて頂きます。

現在、VC++6.0にてスレッドを使用したソケット送受信のプログラムを
作っております。
クライアントは、Windows2000 Pro、サーバは、Solaris8です。

下記のような関係で、それぞれ32〜250バイト程度のデータを送受信します。
サーバ <−> 本プログラム <−>同一クライアントのメインプロセス

下記ソース中の
SvThreadは、サーバからの受信とメインプロセスへの送信
ClThreadは、メインプロセスからの受信とサーバからの送信
を行っています。

サーバからwriteで、ソケットへの書き込みがあり、selectが受信し、
サーバ側スレッド(SvThread)が生成されます。
このとき、サーバからは規定バイト数(例えば100バイト)送信され、
SvThreadのrecvで100バイト受信しているのですが、なぜか複数のスレッドが
生成されてしまいます。(2個の時もあれば、200個の時もある)

私の予想では、SvThreadにて、recvを行っている間(受信し終わっていない
状態で)にselectが、対象ソケットをチェックしてしまいselectを抜けてしまうの
ではないかと思っているのですが・・・。

この場合、一回のサーバからの送信に対しそれを読み終えるまで
selectが反応しない方法はありませんでしょうか?
ご教授をお願いいたします。

〜〜〜〜〜ソース〜〜〜〜〜

int hogehoge(void){

 int   iRet;
 THR_INF  ThreadInf;
 LPTHRINF lpThreadInf = NULL;
 HANDLE  hHdl;
 HANDLE  hEvent[2];
 long  lThreadId;
 unsigned ThreadAddr;
 unsigned long ulRet;

// ・・・・・・・・・・・・・・・・・・・
// ソケット作成やら、bindやら、listenやら
// ・・・・・・・・・・・・・・・・・・・

 while(1){
  //! FD_SET構造体にサーバ側とメイン側のソケット設定
  fd_set  FdSet;
  FD_ZERO(&FdSet);
  FD_SET(ServerFD, &FdSet); // ServerFDはグローバル
  FD_SET(ClientFD, &FdSet); // ClientFDはグローバル

  // 2つのソケットで受信を待ちつづける
  iRet = select(FD_SETSIZE, &FdSet, NULL, NULL, NULL);
  if (iRet <= 0){
   // エラー処理
   return(0);
  }

  // サーバーFDチェック
  if (FD_ISSET(ServerFD, &FdSet) != 0){
   FD_CLR( ServerFD, &FdSet );
   // スレッド生成
   lThreadId = _beginthreadex(0, 0, SvThread, lpThreadInf, 0, &ThreadAddr);
   if (lThreadId == 0){
    //エラー処理
    return(1);
   }
  }else{
    // ghMainEventはグローバル
   ghMainEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   FD_CLR( ClientFD, &FdSet );
   lThreadId = _beginthreadex(0, 0, ClThread, lpThreadInf, 0, &ThreadAddr);
   if (lThreadId == 0){
    // エラー処理
    return(1);
   }
  }
 }
 return(0);
}



以上、よろしくお願いいたします。

dairygoods 2003/02/05(水) 15:15:07
selectをスレッド側で行えばよいのでは?

ボコノン教徒 2003/02/05(水) 15:25:41
> 私の予想では、SvThreadにて、recvを行っている間(受信し終わっていない
> 状態で)にselectが、対象ソケットをチェックしてしまいselectを抜けてしまうの
> ではないかと思っているのですが・・・。

で正しいと思います。ソケットの読み込みバッファにデータが残っている限り、
selectが反応するのでしょう。

> この場合、一回のサーバからの送信に対しそれを読み終えるまで
> selectが反応しない方法はありませんでしょうか?

スレッドが1つしか起動できないようにロックするしかないのでは。

ところで

> iRet = select(FD_SETSIZE, &FdSet, NULL, NULL, NULL);

タイムアウトもエラーイベントも設定して無いようですが、これで
大丈夫ですか?

okuyama 2003/02/05(水) 16:43:54
dairygoods様、ボコノン教徒様、ご返答ありがとうございます。

>> この場合、一回のサーバからの送信に対しそれを読み終えるまで
>> selectが反応しない方法はありませんでしょうか?
>スレッドが1つしか起動できないようにロックするしかないのでは。

このスレッドの起動の制御というのは、どのようにすればできるのでしょうか?

>> iRet = select(FD_SETSIZE, &FdSet, NULL, NULL, NULL);
>タイムアウトもエラーイベントも設定して無いようですが、これで
>大丈夫ですか?

すいません。この辺はこれから勉強します。

ボコノン教徒 2003/02/05(水) 17:00:48
> このスレッドの起動の制御というのは、どのようにすればできるのでしょうか?

セマフォ(CreateSemaphore(),)とか。

okuyama 2003/02/07(金) 00:08:29
[[解決]]
ボコノン教徒様ありがとうございました。

結局のところ、セマフォ、ミューテックスにての起動の制御の仕方が分からず、
今回はWaitForSingleObjectでスレッド終了を待つことにしました。

ちなみにselectのタイムアウトはhogehogeもスレッドとなっており、
その呼び出し側でタイムアウトを取ってるため不要ということになりました。

ありがとうございました。

ボコノン教徒 2003/02/07(金) 09:10:28
> ちなみにselectのタイムアウトはhogehogeもスレッドとなっており、
> その呼び出し側でタイムアウトを取ってるため不要ということになりました。

タイムアウトしたらスレッドhogehogeをKILLしちゃう、ということですよね。
selectを抜けた瞬間にKILLされちゃったりするとか、色々問題を残しそうです。
”潜在的バグ”になっちゃいそうな気がします。

dairygoods 2003/02/07(金) 10:03:52
>今回はWaitForSingleObjectでスレッド終了を待つことにしました。

スレッドを起動して、スレッドの終了を待つのであれば、
別スレッドで処理する意味は無いと思いますが。

古いスレッドには追加発言できません。