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