マルチスレッド処理 マルチプロセス処理について 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
© Copyright 2024 ExpyDoc