Talkプログラムのヒント

マルチスレッド処理
マルチプロセス処理について
CS-B3 ネットワークプログラミング
&情報科学科実験I
1
このスライドについて
このスライドでは皆さんがプログラムを書いたり,関数を調べたり
する過程で行き詰ると予想される部分について簡単に解説します
このスライドの目的は自主学習のサポートであり,説明が簡略化さ
れています.完全な理解には自主学習が必要なので注意してくださ
い.
2
目次







よくつまずくところ
解決方法の例
解決方法の例 その1
スレッドの動作
解決方法の例 その2
プロセスの動作
まとめ
3
よくつまずくところ
以下のようなプログラムのままになっていませんか?
while(1) {
fgets()でキー入力を取得;
キー入力しなければ
次に進めない
send()で入力文字を送信;
送っている最中は
キー入力,受信ができない
受信待ちの間は
キー入力,送信ができない
recv()で相手からメッセージを受け取る;
}
4
解決方法の例
send()もrecv()も独立して別々に処理してくれれば
楽ですよね?
前処理
while(1) {
fgets()でキー入力を取得;
send()で入力文字を送信;
}
while(1) {
recv()する;
}
後処理
5
解決方法の例 その1
以下のような実装方法(スレッド)はどうですか?
前処理
send()用スレッド
recv()用スレッド
後処理
6
スレッドの動作
1スレッド = 1関数
•スレッドは指定された関数を1つ実行する
(main()関数がもう一個あるような感覚)
•必要な値は,構造体の中に保存してスレッド用関数に渡せる
•スレッド用関数内で,別の関数を利用することも可能
7
スレッドの作成について
pthread.hのヘッダファイルをインクルード
pthread_create関数でスレッドを作成
pthreadを使う際の幾つか注意点・・・
・終了した子スレッドはpthread_join関数で解放
・もしくはpthread_detach関数でデタッチしておく
・親⇒子の強制終了はデッドロックの可能性がある
・スレッド間通信には排他処理が必要となる
などなど
その他pthreadライブラリ関数について
http://d.hatena.ne.jp/gikogeek/20070703や
http://ja.wikipedia.org/wiki/POSIX%E3%82%B9%E3%83%AC
%E3%83%83%E3%83%89
などを参照
8
int main() {
/* send用スレッド作成 */
pthread_create(&SendthreadID,
NULL,
SendThreadMain,
(void *)SendthreadArgs);
処理
/* recv用スレッド作成 */
pthread_create(&RecvthreadID,
NULL,
RecvThreadMain,
(void *)RecvthreadArgs);
各種処理;
return 0;
}
/* Sendスレッド用関数 */
void *SendThreadFunc(void *threadArgs) {
処理;
return 0;
}
/* Recvスレッド用関数 */
void *RecvThreadFunc(void *threadArgs) {
処理;
return 0;
}
9
int main() {
/* send用スレッド作成 */
pthread_create(&SendthreadID,
NULL,
SendThreadMain,
(void *)SendthreadArgs);
/* recv用スレッド作成 */
pthread_create(&RecvthreadID,
NULL,
RecvThreadMain,
(void *)RecvthreadArgs);
処理
新スレッド1
SendThreadMainの処理{
処理
処理;
return 0;
}
各種処理;
return 0;
}
/* Sendスレッド用関数 */
void *SendThreadFunc(void *threadArgs) {
処理;
return 0;
}
/* Recvスレッド用関数 */
void *RecvThreadFunc(void *threadArgs) {
処理;
return 0;
}
10
int main() {
/* send用スレッド作成 */
pthread_create(&SendthreadID,
NULL,
SendThreadMain,
(void *)SendthreadArgs);
/* recv用スレッド作成 */
pthread_create(&RecvthreadID,
NULL,
RecvThreadMain,
(void *)RecvthreadArgs);
新スレッド1
処理
SendThreadMainの処理{
処理
処理;
return 0;
}
各種処理;
return 0;
}
/* Sendスレッド用関数 */
void *SendThreadFunc(void *threadArgs) {
処理;
return 0;
}
/* Recvスレッド用関数 */
void *RecvThreadFunc(void *threadArgs) {
処理;
return 0;
}
11
int main() {
/* send用スレッド作成 */
pthread_create(&SendthreadID,
NULL,
SendThreadMain,
(void *)SendthreadArgs);
/* recv用スレッド作成 */
pthread_create(&RecvthreadID,
NULL,
RecvThreadMain,
(void *)RecvthreadArgs);
新スレッド1
処理
SendThreadMainの処理{
処理
処理;
return 0;
}
各種処理;
return 0;
}
/* Sendスレッド用関数 */
void *SendThreadFunc(void *threadArgs) {
処理;
return 0;
}
新スレッド2
RecvThreadMainの処理{
処理
処理;
return 0;
}
/* Recvスレッド用関数 */
void *RecvThreadFunc(void *threadArgs) {
処理;
return 0;
}
12
int main() {
/* send用スレッド作成 */
pthread_create(&SendthreadID,
NULL,
SendThreadMain,
(void *)SendthreadArgs);
新スレッド1
SendThreadMainの処理{
処理
処理;
return 0;
}
/* recv用スレッド作成 */
pthread_create(&RecvthreadID,
NULL,
RecvThreadMain,
(void *)RecvthreadArgs);
処理
各種処理;
新スレッド2
return 0;
}
/* Sendスレッド用関数 */
void *SendThreadFunc(void *threadArgs) {
処理;
return 0;
}
/* Recvスレッド用関数 */
void *RecvThreadFunc(void *threadArgs) {
処理;
return 0;
}
RecvThreadMainの処理{
処理
処理;
return 0;
}
※具体的な実装方法は自主的に調べてみましょう13
解決方法の例 その2
以下のような実装方法(プロセス)はどうですか?
前処理
send()用プロセス
recv()用プロセス
後処理
14
プロセスの動作
プロセス = プログラムのクローン
•プロセスを作成すると,そのプログラムのコピーが作られる
•コピーされたプログラム(子プロセス)の実行開始位置は,先
頭からではなく,元々のプログラム(親プロセス)と同じ続きの
位置から
= 親プロセス,子プロセスで以降の処理を分岐させる
•変数の値も子プロセス作成時点のものがコピーされる (別領域)
15
プロセスの作成について
fork関数でスレッドを作成
fork関数の返り値で自身が親か子を判定できる
forkを使う際の幾つか注意点・・・
・終了した子プロセスはwait(pid)関数で解放
⇒これを怠るとゾンビプロセスが発生する
・各プロセスの変数領域は異なっている
⇒そのままではプロセス間で変数を共有できない
⇒解決策:共有メモリ・パイプなど
などなど
16
プロセスの動作
例えば…
親プロセス
int main() {
/* 子プロセスを作成 */
pid = fork();
処理
if (親プロセス) {
親プロセスの処理;
}
else if (子プロセス) {
子プロセスの処理;
}
return 0;
}
17
プロセスの動作
例えば…
子プロセス
親プロセス
int main() {
int main() {
/* 子プロセスを作成 */
pid = fork();
/* 子プロセスを作成 */
pid = fork();
if (親プロセス) {
親プロセスの処理;
}
else if (子プロセス) {
子プロセスの処理;
}
return 0;
}
if (親プロセス) {
親プロセスの処理;
}
処理
処理
else if (子プロセス) {
子プロセスの処理;
}
return 0;
}
18
プロセスの動作
例えば…
子プロセス
親プロセス
int main() {
int main() {
/* 子プロセスを作成 */
pid = fork();
/* 子プロセスを作成 */
pid = fork();
if (親プロセス) {
親プロセスの処理;
}
if (親プロセス) {
親プロセスの処理;
}
else if (子プロセス) {
子プロセスの処理;
}
return 0;
}
処理
else if (子プロセス) {
子プロセスの処理;
}
return 0;
処理
}
19
プロセスの動作
例えば…
子プロセス
親プロセス
int main() {
int main() {
/* 子プロセスを作成 */
pid = fork();
/* 子プロセスを作成 */
pid = fork();
if (親プロセス) {
親プロセスの処理;
}
if (親プロセス) {
親プロセスの処理;
}
else if (子プロセス) {
子プロセスの処理;
}
return 0;
}
処理
else if (子プロセス) {
子プロセスの処理;
}
return 0;
処理
}
※具体的な実装方法の詳細は各自で調べてみましょう
20
まとめ
マルチスレッド処理・マルチプロセス処理によって
プログラムをsend()用とrecv()用に分けることで,
送受信を並行して行うことができる
プロキシプログラムではselectとマルチスレッド処理・マル
チプロセス処理の複数を使うことになるかもしれません
このスライドのヒントはあくまでも実装方法の一例です
(他の実装方法もあるかもしれません)
以上の内容をヒントにして,自主学習やプログラムの実装を進めてみ
てください
21