マルチスレッド対応のクライアントプログラムでは、RPC 要求が出されるたびにスレッドを 1 つ作成することができます。複数スレッドが同一のクライアントハンドルを共有する場合は、RPC 要求を発行できるのは一度に 1 つのスレッドだけです。その他のすべてのスレッドは、未処理の要求が終了するまで待たなければなりません。これに対して、複数スレッドがそれぞれ固有のクライアントハンドルを使用して RPC 要求を出す場合には、複数の要求が同時に処理されます。図 4-2 は、異なるクライアントハンドルを使用するクライアント側の 2 つのスレッドから成るマルチスレッド対応クライアント環境でのタイミングの例を示したものです。
例 4-38 は、クライアント側でマルチスレッド rstat プログラムを実行する場合を示します。クライアントプログラムは各ホストに対してスレッドを作成します。スレッドはそれぞれ、固有のクライアントハンドルを作成し、指定のホストにさまざまな RPC 呼び出しを行なっています。クライアント側の各スレッドは異なるハンドルを使用して RPC 呼び出しを行うため、RPC 呼び出しは同時に実行されます。
RPC マルチスレッド対応アプリケーションを作成する場合は常に、スレッドライブラリをリンクしなければなりません。コンパイルコマンドで -lthread を指定して、スレッドライブラリを最後にリンクするようにしなければなりません。
次のように入力して 例 4-38 のプログラムを作成します。
$ cc rstat.c -lnsl -lthread |
/* @(#)rstat.c 2.3 93/11/30 4.0 RPCSRC */ /* * w コマンドと同様の形式で、遠隔ホストのステータスを表示する簡単な * プログラム */ #include <thread.h> /* スレッドインタフェースの定義 */ #include <synch.h> /* 相互排他的ロックの定義 */ #include <stdio.h> #include <sys/param.h> #include <rpc/rpc.h> #include <rpcsvc/rstat.h> #include <errno.h> mutex_t tty; /* printf のための tty の制御 */ cond_t cv_finish; int count = 0; main(argc, argv) int argc; char **argv; { int i; thread_t tid; void *do_rstat(); if (argc < 2) { fprintf(stderr, "usage: %s ¥"host¥" [...]¥n", argv[0]); exit(1); } mutex_lock(&tty); for (i = 1; i < argc; i++) { if (thr_create(NULL, 0, do_rstat, argv[i], 0, &tid) < 0) { fprintf(stderr, "thr_create failed: %d¥n", i); exit(1); } else fprintf(stderr, "tid: %d¥n", tid); } while (count < argc-1) { printf("argc = %d, count = %d¥n", argc-1, count); cond_wait(&cv_finish, &tty); } exit(0); } bool_t rstatproc_stats(); void * do_rstat(host) char *host; { CLIENT *rstat_clnt; statstime host_stat; bool_t rval; struct tm *tmp_time; struct tm host_time; struct tm host_uptime; char days_buf[16]; char hours_buf[16]; mutex_lock(&tty); printf("%s: starting¥n", host); mutex_unlock(&tty); /* rstat クライアントハンドル */ rstat_clnt = clnt_create(host, RSTATPROG, RSTATVERS_TIME, "udp"); if (rstat_clnt == NULL) { mutex_lock(&tty); /* ty の制御権を取得 */ clnt_pcreateerror(host); count++; cond_signal(&cv_finish); mutex_unlock(&tty); /* tty の制御権を解放 */ thr_exit(0); } rval = rstatproc_stats(NULL, &host_stat, rstat_clnt); if (!rval) { mutex_lock(&tty); /* tty の制御権を取得 */ clnt_perror(rstat_clnt, host); count++; cond_signal(&cv_finish); mutex_unlock(&tty); /* tty の制御権を解放 */ thr_exit(0); } tmp_time = localtime_r(&host_stat.curtime.tv_sec, &host_time); host_stat.curtime.tv_sec = host_stat.boottime.tv_sec; tmp_time = gmtime_r(&host_stat.curtime.tv_sec, &host_uptime); if (host_uptime.tm_yday != 0) sprintf(days_buf, "%d day%s, ", host_uptime.tm_yday, (host_uptime.tm_yday > 1) ? "s" : ""); else days_buf[0] = '¥0'; if (host_uptime.tm_hour != 0) sprintf(hours_buf, "%2d:%02d,", host_uptime.tm_hour, host_uptime.tm_min); else if (host_uptime.tm_min != 0) sprintf(hours_buf, "%2d mins,", host_uptime.tm_min); else hours_buf[0] = '¥0'; mutex_lock(&tty); /* tty の制御権を取得 */ printf("%s: ", host); printf(" %2d:%02d%cm up %s%s load average: %.2f %.2f %.2f¥n", (host_time.tm_hour > 12) ? host_time.tm_hour - 12 : host_time.tm_hour, host_time.tm_min, (host_time.tm_hour >= 12) ? 'p' : 'a', days_buf, hours_buf, (double)host_stat.avenrun[0]/FSCALE, (double)host_stat.avenrun[1]/FSCALE, (double)host_stat.avenrun[2]/FSCALE); count++; cond_signal(&cv_finish); mutex_unlock(&tty); /* tty の制御権を解放 */ clnt_destroy(rstat_clnt); sleep(10); thr_exit(0); } /* クライアント側の MT rstat プログラムの実行 */ /* clnt_control() を使用してデフォルトのタイムアウトを変更可能 */ static struct timeval TIMEOUT = { 25, 0 }; bool_t rstatproc_stats(argp, clnt_resp, clnt) void *argp; statstime *clnt_resp; CLIENT *clnt; { memset((char *)clnt_resp, 0, sizeof (statstime)); if (clnt_call(clnt, RSTATPROC_STATS, (xdrproc_t) xdr_void, (caddr_t) argp, (xdrproc_t) xdr_statstime, (caddr_t) clnt_resp, TIMEOUT) != RPC_SUCCESS) { return (FALSE); } return (TRUE); } |