スレッドに構造体を受け取って貰う方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
shiro4ao
記事: 224
登録日時: 15年前
住所: 広島

スレッドに構造体を受け取って貰う方法

#1

投稿記事 by shiro4ao » 14年前

winsockでクライアントとサーバーの間に入って
通信を手助けしてあげるソフト(プロクシに近い挙動です)を作っています

クライアントが何人も来るのでスレッドで通信を仲介してあげようと思ったのですが
スレッドにソケットをまとめた構造体を渡すとセグメンテーション違反で落ちてしまいます。
多分引数の渡し方がおかしいのだと思うのですが正しい方法がわかりません。

なにか解決方法はありますでしょうか。

コード:

//スレッドには1つしか引数が渡せないので構造体にして複数渡す
typedef struct {
	 SOCKET sClient;
	 SOCKET sServer;
} SOCKTABLE;


//スレッド1
DWORD WINAPI thread(void* lpvoid)
{
	SOCKTABLE *st;

	st=(SOCKTABLE*)lpvoid;
	ret = recv(st->sClient, buf, 1000,0);
  	send(st->sServer,buf,ret,0);
	return 0;

}

//ここが呼び出される
 void StartService(SOCKET sClient)
{
	SOCKET sServer;
	SOCKTABLE *st;


    sServer=ConnectToServer();
	st->sClient=sClient;                   //CreateThreadがあるとここで落ちる
	st->sServer=sServer;
	
	CreateThread(NULL , 0 , thread ,(LPVOID)st, 0 ,NULL);	
	
	return ;
		
}	
 

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: スレッドに構造体を受け取って貰う方法

#2

投稿記事 by softya(ソフト屋) » 14年前

構造体ポインタでメモリが確保されていない。
SOCKTABLE *st;

あと今後やりそうなミスで、ローカル変数をスレッドに渡さないこと。

[追記]
とにかく、基本的な所でミスしていますので構造体ポインタを勉強されることをお勧めします。
「C言語入門 11.構造体」
http://c-production.com/contents/c/sec11.html
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
shiro4ao
記事: 224
登録日時: 15年前
住所: 広島

Re: スレッドに構造体を受け取って貰う方法

#3

投稿記事 by shiro4ao » 14年前

ご紹介していただいたサイトで構造体本体を忘れていることはわかったのですが
softya(ソフト屋) さんが書きました: あと今後やりそうなミスで、ローカル変数をスレッドに渡さないこと。
という部分がよくわかりませんでした。

ローカル変数は関数から抜けると消えるからその変数へのポインタは意味がなくなる
という意味でしょうか?

正しい方法かわかりませんが、スレッドで変数へのポインタが無効になる前にポインタごしに
値をコピーして(値渡しして)引き続き使うような方法はあるのでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: スレッドに構造体を受け取って貰う方法

#4

投稿記事 by softya(ソフト屋) » 14年前

現状の問題は、構造体ポインタ変数のポインタ値が未初期化なのでポインタの指すメモリがありません。
なのでメモリがない構造体メンバへ代入すればを実行時エラーになります。
shiro4ao さんが書きました:ローカル変数は関数から抜けると消えるからその変数へのポインタは意味がなくなる
という意味でしょうか?
ローカル変数の構造体を作ってポインタにしてスレッドに渡すのはNGと言う意味です。
構造体のメモリを取れば良いという意味に解釈しそうな気がしたので釘を刺しておきました。
shiro4ao さんが書きました: 正しい方法かわかりませんが、スレッドで変数へのポインタが無効になる前にポインタごしに
値をコピーして(値渡しして)引き続き使うような方法はあるのでしょうか?
スレッドの数だけSOCKTABLE構造体変数が必要なこと考えると
方法(1)最大同時進行スレッド数を決めてSOCKTABLE構造体の配列から空いている要素を割り当てる。
方法(2)必要に応じてmallocやnew(c++なら)で割り当てる
などの方法があります。他にもリスト構造で管理するなどの方法もありますけどね。
どちらにしても不要になった場合の割り当て解放処理は必須です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: スレッドに構造体を受け取って貰う方法

#5

投稿記事 by ISLe » 14年前

ローカル変数かどうかというよりか、スレッド関数の引数はポインタなので、呼び出し側で構造体変数を使い回したりすると、スレッド側からしたら突然中身が変わってしまうことになるので、スレッドごとに確保した構造体変数を渡す必要がある、ということですね。

スレッド側でコピーするのは実行順序の問題にぶつかるので、呼び出し側で確保するほうが楽です。

アバター
shiro4ao
記事: 224
登録日時: 15年前
住所: 広島

Re: スレッドに構造体を受け取って貰う方法

#6

投稿記事 by shiro4ao » 14年前

ご回答ありがとうございます。

>softyaさん
スレッドに渡すだけで済むのであればずいぶん楽になると思ったのですが、
やっぱりちゃんと管理してあげないといけないのですね。

>ISLe
準備してあげるのも呼び出し側の仕事なのですね・・・
長期的にはクラスにでもラップして使おうと思います

さしあたっては、グローバルな配列をつくってそこでソケット達を管理しようと思います。
ありがとうございました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: スレッドに構造体を受け取って貰う方法

#7

投稿記事 by softya(ソフト屋) » 14年前

shiro4ao さんが書きました:ご回答ありがとうございます。

>softyaさん
スレッドに渡すだけで済むのであればずいぶん楽になると思ったのですが、
やっぱりちゃんと管理してあげないといけないのですね。

>ISLe
準備してあげるのも呼び出し側の仕事なのですね・・・
長期的にはクラスにでもラップして使おうと思います

さしあたっては、グローバルな配列をつくってそこでソケット達を管理しようと思います。
ありがとうございました。
グローバルな配列をどう管理するかすごく気になるのですが。
クリティカルセクションとかセマフォとかも勉強されたほうが良いと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
shiro4ao
記事: 224
登録日時: 15年前
住所: 広島

Re: スレッドに構造体を受け取って貰う方法

#8

投稿記事 by shiro4ao » 14年前

クリティカルセクションは使ったことがあるのですが、
セマフォは使う状況がよくわかりません。
特定の人数までしかアクセスできない場合につかうそうですが・・・?

ソケットをまとめて管理してる配列にアクセスできる人は一人であるべきなので
クリティカルセクションで操作すればいいかなと思います

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: スレッドに構造体を受け取って貰う方法

#9

投稿記事 by softya(ソフト屋) » 14年前

shiro4ao さんが書きました:クリティカルセクションは使ったことがあるのですが、
セマフォは使う状況がよくわかりません。
特定の人数までしかアクセスできない場合につかうそうですが・・・?

ソケットをまとめて管理してる配列にアクセスできる人は一人であるべきなので
クリティカルセクションで操作すればいいかなと思います
実のところ、うまく管理すればセマフォやクリティカルセクションなど同期処理も必要ない状態に出来ます。グローバル参照な配列である必要性もありません。逆にグローバルだとミスする可能性が高くなるので危険かもしれません。
使っている状態にするのが常にmainで解放状態にするのがスレッドと限定すれば、mainがうっかり未解放の配列要素を使ってしまう事故は防げます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
shiro4ao
記事: 224
登録日時: 15年前
住所: 広島

Re: スレッドに構造体を受け取って貰う方法

#10

投稿記事 by shiro4ao » 14年前

終わったトピックですみません
CreateThreadの第4引数ってなんなんでしょう
もう使われないきのうだったりするのでしょうか・・・?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: スレッドに構造体を受け取って貰う方法

#11

投稿記事 by softya(ソフト屋) » 14年前

shiro4ao さんが書きました:終わったトピックですみません
CreateThreadの第4引数ってなんなんでしょう
もう使われないきのうだったりするのでしょうか・・・?
CreateThread(NULL , 0 , thread ,(LPVOID)st, 0 ,NULL);
の(LPVOID)stの部分ですよね?
意味もわからずに使われていたのでしょうか?
スレッドごとに別の引数を渡すには第4引数を使うしか無いのですが。
「CreateThread 関数」
http://msdn.microsoft.com/ja-jp/library/cc429080.aspx

あっと書き忘れてましたが、CreateThreadよりも_beginthreadexを使ったほうが良いです。
これにしないとスレッドで使えないC言語の関数が出てくるので。
「_beginthread、_beginthreadex (CRT)」
http://msdn.microsoft.com/ja-jp/library ... s.80).aspx

●参考
「スレッドを調べまわって分かったことをメモ - かせいさんとこ」
http://d.hatena.ne.jp/kasei_san/20080403/p1
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
shiro4ao
記事: 224
登録日時: 15年前
住所: 広島

Re: スレッドに構造体を受け取って貰う方法

#12

投稿記事 by shiro4ao » 14年前

softya(ソフト屋) さんが書きました: CreateThread(NULL , 0 , thread ,(LPVOID)st, 0 ,NULL);
の(LPVOID)stの部分ですよね?
意味もわからずに使われていたのでしょうか?
スレッドごとに別の引数を渡すには第4引数を使うしか無いのですが。
ここがよくわかりませんでした。すみません。

CreateThread第4引数に書いておけばスレッドの関数内で
受け取ることができると認識していました

しかし
スレッドごとに別の引数を渡すには第4引数を使うのに
今回のように通信用ソケット構造体ポインタを渡すのはNG

参照渡しがNGで値渡しならOKなのでしょうか?
(値渡しなら引数になってた変数がどうなろうが大丈夫そうに見えます)
でもLPVOID型ですし文法上はどうすればいいのか・・・・・

自分が何かしら大きな誤解をして覚えているような気がします・・・
大変申し訳ありません・・・・

beginthreadexには変えていきたいとは思っています・・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: スレッドに構造体を受け取って貰う方法

#13

投稿記事 by softya(ソフト屋) » 14年前

shiro4ao さんが書きました:しかし
スレッドごとに別の引数を渡すには第4引数を使うのに
今回のように通信用ソケット構造体ポインタを渡すのはNG

参照渡しがNGで値渡しならOKなのでしょうか?
(値渡しなら引数になってた変数がどうなろうが大丈夫そうに見えます)
でもLPVOID型ですし文法上はどうすればいいのか・・・・・
(1)最初のものは構造体ポインタの指すの構造体の実体メモリを確保していなかったのでNGです。

コード:

    SOCKTABLE *st; ← 構造体ポインタであって、構造体メモリ自体は未確保。ポインタ自体はローカル変数で問題なし。
     sServer=ConnectToServer();
    st->sClient=sClient;                   //CreateThreadがあるとここで落ちる
    st->sServer=sServer;
(2)次に問題になったのは同じ構造体をもしスレッドに渡してしまうとスレッドが参照中に値が書き変わる危険性があるので避けるようにと言う話です。

で、LPVOID型だとポインタしか渡せませんので構造体を全体を値渡しで渡すことは出来ません。なので、ポインタを引数にするしか無いと言う結論で間違いありません。
構造体ポインタに入れる構造体変数のポインタ値は、ローカル変数の構造体ではないこと、(2)で書いたようにスレット毎に違う構造体変数のメモリを確保することが条件となります。
と言うことから、構造体配列から未使用の構造体をポインタでスレッドに渡したり、毎回mallocで構造体のメモリを確保してスレッドに渡したら?と言う話になったわけです。

[補足] ずーっと勘違いされている感じが続いていたので不安に思ってました。
最初から読みなおして疑問に思った点は質問してみて下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
shiro4ao
記事: 224
登録日時: 15年前
住所: 広島

Re: スレッドに構造体を受け取って貰う方法

#14

投稿記事 by shiro4ao » 14年前

長々とすみません。

(1)最初のコードはポインタの差す構造体本体が無いのでNG
(2)同じ構造体を使いまわすと2個目のスレッドが勝手に書き換えるのでNG
(3)CreateThread第4引数にはLPVOIDしか入らないので値渡しは無理
よって、予めソケットテーブル用の構造体配列を幾つか作っておいて
必要に応じてそこから使う以外に手はない

という感じでしょうか。

CreateThreadが値渡しもしてくれれば一番楽なんですが・・・・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: スレッドに構造体を受け取って貰う方法

#15

投稿記事 by softya(ソフト屋) » 14年前

shiro4ao さんが書きました:長々とすみません。

(1)最初のコードはポインタの差す構造体本体が無いのでNG
(2)同じ構造体を使いまわすと2個目のスレッドが勝手に書き換えるのでNG
(3)CreateThread第4引数にはLPVOIDしか入らないので値渡しは無理
よって、予めソケットテーブル用の構造体配列を幾つか作っておいて
必要に応じてそこから使う以外に手はない

という感じでしょうか。

CreateThreadが値渡しもしてくれれば一番楽なんですが・・・・・・
そうですね。そういう事です。
そういう所を使う人が決めれるのがC/C++の長所でもあり短所でもあります。
JavaやVBやC#であれば、また違う形で渡すことは可能ですし、そもそもCreateThreadの様なスレッドの呼び出し方をしない書き方ができます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

閉鎖

“C言語何でも質問掲示板” へ戻る