dポイントプレゼントキャンペーン実施中!

C言語の並列処理について調べているのですが、並列処理はfor文などの単調な処理を並列処理にすることで実行を早くするということがわかったのですが、実行ファイル内で同時に二つの違う動作をさせることは可能なのでしょうか?

A 回答 (6件)

#5 です。

サンプルプログラムが長くなってしまった関係で、コメントは別にさせていただきました。

 まず、最初に↓の pdf ファイルをご覧になってください。
www.xfile09.com/fig/algo1.pdf

>並列処理はfor文などの単調な処理を並列処理にすることで実行を早くするということがわかったのですが、

これは data parallelism と呼ばれているテーマです。

>実行ファイル内で同時に二つの違う動作をさせることは可能なのでしょうか?

一方、これは task parallelism と呼ばれているテーマです。
http://techon.nikkeibp.co.jp/article/TOPCOL/2008 …

 巷では、同時に二つ以上の違う動作を行うには、セマフォやミューテックスなどが必須であって、その実現は困難であるとされ、アメリカが躍起になっている分野です。
http://www.nytimes.com/2008/04/30/technology/30l …

この問題は現在、難解な問題と捉えられていますが、考えを自由にプログラミングができる線形思考から、もはや自由にプログラミングはできない非線形思考へと考えを変えることで、簡単に対応可能です。また、新たなプログラミング言語を覚える必要もありません(C言語のままでOK)。
 その証明として、特に Mac OSX を講義に使われている京都産業大学や琉球大学、また九州大学や東京大学の学生の方が実際に現実を体験していただきたく、ここにC言語による同時ソーティングによるサンプルプログラムを 回答番号No. 5 示します。UNIX系である Linuxユーザーの方も体験できるでしょう。

 ワーキングディレクトリ内に以下の3つのプログラムを置き、sort_main.c 、sort_sub.c の2つをコンパイルしてください。実行は、シェルのコマンドラインから、
  ./a.out
と入力し、起動してください。

 sort_mainプログラムが ./a.out によって起動されることから親プロセスとなり、この親プロセスが sort_sub プログラムを「&」の並列処理のために起動されます。すなわち、sort_sub は子プロセスという立場にあります。
 親プロセスは、子プロセスのバブルソートよりは処理が速いシェルソートを使うことにし、速度の違いによってどのような同時実行の違い(ハード的違い)があるのかを見てわかるようにしてみました。
 親子の関係は、共有メモリの掲示板(sorting.h)と親から子へ、子から親への声を掛け合う(シグナル)ことで実現しています。それ以外の道具は必要ありません。
 子プロセスは、起動後、親プロセスに対して「 用意できたよ」と掲示板に書いて声を掛け( signal送信)、親プロセスの返事を待ちます( pause() )。その子プロセスに対して親プロセスは、「わかったよ」と返事をしてあげる( kill() signal送信)ことで2つのプログラムは同時に動き始めるのです。
 Macの場合は、アクティビティモニタを先に開いてから、./a.out を入力してください。プロセスの稼働状況がどのようになっているのか、目で確認してください。わからなければ、何度も ./a.out を実行し、アクティビティモニタを見れば良いでしょう。この並列処理の実現に際し、線形プログラミングに使われるセマフォやミューテックスは一切使っていないシンプルなプログラミング構造であることを確認してください。また、並列処理はC言語によって実現されていることを確認してください。
    • good
    • 0

/* 共通事項リストアップ・定義等ヘッダーファイル


* file name: sorting.h
*
*プロセス間での共通項を列挙し、不用意な間違いを少なくする。
*Cの線形構造を利用する。
*/

#define PROCS 2/* 全プロセス数(Dual; core) */
#define CHILD 1/* 子プロセス1 */
#define PARENT 0/* 親プロセス */
#define ON 1
#define OFF 0
#define N 100000/* 10万個 */

struct set1 {/* common variable */
int dat[N], temp[N];/* data buffer */
signed char flag[PROCS];/* condition flag */
pid_t id_number[PROCS];/* id code */
} *ptr;




/******* ここまで **********/




/* A sample program of task parallelism by Gcc on Mac OSX
* file name: sort_main.c
* compile: gcc sort_main.c
* execution: ./a.out
*/

#include <stdio.h>/* for printf() */
#include <unistd.h>/* for getpid() */
#include <stdlib.h>/* for exit() */
#include <string.h>
#include <time.h>
#include <sys/types.h>/* for shared memory */
#include <sys/ipc.h>/* 〃 */
#include <sys/shm.h>/* 〃 */
#include <sys/stat.h>
#include <signal.h>/* for signal */
#include <errno.h>
#include "sorting.h"/* 共通 struct構造体 */

#define CODE 999/* 適当に変更のこと */

void shell_sort(int *, int);
void pri_out(char *, int *, char *);
double second(void);
void handler(int);

int main(void) {
int shmid;/* 共有メモリ関連 */
void *shmaddr;/* 〃 */
char command_line[64];
int i;
double start;


/* 共有メモリの確保 */
if (((shmid = shmget(CODE, sizeof(*ptr) + 2, IPC_CREAT | S_IRUSR | S_IWUSR)) == -1)
|| ((shmaddr = shmat(shmid, NULL, 0)) == (char *)-1)) {
perror("Shared memory set");
exit(1);
}

/* 共有メモリに struct構造体をあてがう */
ptr = (struct set1 *)shmaddr;

/* 自身プロセスIdを記録する */
ptr->id_number[PARENT] = getpid();

/* シグナルハンドラー登録 */
signal(SIGCONT, handler);

/* 共有メモリに乱数を用意 */
srand(time(NULL));
for(i=0;i<N;i++)
ptr->temp[i] = ptr->dat[i] = (int)rand() % N + 1;

/* 乱数データの出力 */
pri_out("Random data:", ptr->temp, "\n\n");

/* バックグランドで同時計算可に*/
ptr->flag[PARENT] = ON;// 子プロセス完了まで待つ
sprintf(command_line, "./sort_sub %d &", shmid);
if (system(command_line) != 0) {
perror("Shell command line error");
exit(1);
}
/* 子プロセスの順延に配慮 */
fprintf(stderr, "Program setting now");
while (ptr->flag[PARENT] != OFF) {
fprintf(stderr, ".");
sleep(1);
}

/* 子プロセスへ並列計算するようシグナルを送信 */
ptr->flag[PARENT] = ON;// 計算状態に有り
ptr->flag[CHILD] = OFF;// 並列計算せよ
kill(ptr->id_number[CHILD], SIGCONT);

/* 乱数データをソーティング */
start=second();
shell_sort(ptr->temp, N);

/* 結果の出力 */
fprintf(stderr, "\n");
printf("The executed time of %d data by Shell sort is %.5f sec.\n", N, second() - start);
pri_out("\n\nSorted data:", ptr->temp, "");

/* 計算終了まで待機(dead_lockの回避)
*作業継続入力後も子プロセスが処理中なら1秒休む。
*ただし、処理が完了したならシグナル等で起こされる。
*/
while (ptr->flag[PARENT] != OFF) {// 子プロセスが操作
fprintf(stderr, "."), fflush(stderr);
sleep(1);
}
fprintf(stderr, "\n");

/* 子プロセスのプログラム終了送信 */
ptr->flag[CHILD] = ON;// 計算終了せよ
kill(ptr->id_number[CHILD], SIGCONT);

/* 共有メモリの解除 */
if (shmdt(shmaddr) == -1 ||
shmctl(shmid, IPC_RMID, 0) == -1) {
perror("Shared memory closed");
exit(1);
}

return 0;
}

/* シェルソート */
void shell_sort(int d[], int n) {
int i, j;
int temp;
for (i = 1; i < n; i++) {
temp = d[i];
for (j = i - 1; j >= 0 && d[j] > temp; j--)
d[j + 1] = d[j];
d[j + 1] = temp;
}
}


void pri_out(char *comment, int a[], char *tail) {
int i;
fprintf(stderr, "%s\n", comment);
for (i = 0; i < 100; i++)
fprintf(stderr, "%d ", a[i]);
fprintf(stderr, " .... ");
for (i = N-100; i < N; i++)
fprintf(stderr, "%d ", a[i]);
fprintf(stderr, "%s\n", tail);
}


/* 稼働時間測定 */
double second() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1.0e6;
}


/* 何もしないシグナル処理関数 */
void handler(int dummy){
;
}




/******* ここまで **********/





/* A sample program of task parallelism by Gcc on Mac OSX
* file name: sort_sub.c
* compile: gcc sort_sub.c -o sort_sub
* execution: Don't execute from command line!!
*/

#include <stdio.h>/* for printf() */
#include <unistd.h>/* for getpid() */
#include <stdlib.h>/* for exit() */
#include <sys/types.h>/* for shared memory */
#include <sys/ipc.h>/* 〃 */
#include <sys/shm.h>/* 〃 */
#include <sys/stat.h>
#include <signal.h>/* for signal */
#include <errno.h>
#include "sorting.h"/* 共通 struct構造体 */

void bubble_sort(int *, int);
double second(void);
void handler(int);

int main(int argc, char *argv[]) {
int shmid;/* 共有メモリ関連 */
void *shmaddr;/* 〃 */
double start;

/* 親プロセスから受け継ぐ */
shmid = atoi(argv[--argc]);
if ((shmaddr = shmat(shmid, NULL, 0)) == (char *)-1) {
perror("Shared menory set");
exit(1);
}

/* 共有メモリに struct構造体をあてがう */
ptr = (struct set1 *)shmaddr;

/* 自身プロセスIdを記録する */
ptr->id_number[CHILD] = getpid();
//ptr->flag[CHILD] = OFF;

/* シグナルハンドラー登録 */
signal(SIGCONT, handler);

/* 親プロセスに「スタンバイ ok!」を知らせる */
ptr->flag[PARENT] = OFF;// スタンバイ ok
kill(ptr->id_number[PARENT], SIGCONT);

while (1) {
/* 親プロセスからのシグナルを待つ */
pause();

if (ptr->flag[CHILD] != OFF)// 計算終了
break;
else {// 計算開始
fprintf(stderr, " And sub program runninng now. ");
fflush(stderr);
start = second();
bubble_sort(ptr->dat, N);// バブルソート
fprintf(stderr, "\n");
printf("The executed time of %d data by Bubble sort is %.5f sec.\n", N, second() - start);
ptr->flag[PARENT] = OFF;// 計算状態が終了
kill(ptr->id_number[PARENT], SIGCONT);
}
}

if (shmdt(shmaddr) == -1) {
perror("Shared menory closed");
exit(1);
}

return 0;
}


/* バブルソート */
void bubble_sort(int d[], int n) {
int i, j, temp;
for (i = 0; i < n - 1; i++) {
for (j = n - 1; j > i; j--) {
if (d[j - 1] > d[j]) {
temp = d[j];
d[j] = d[j - 1];
d[j - 1]= temp;
}
}
}
}


/* 稼働時間測定 */
double second() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1.0e6;
}


/* 何もしないシグナル処理関数 */
void handler(int dummy){
;
}
    • good
    • 0

>実行ファイル内で同時に二つの違う動作をさせることは可能なのでしょうか?


これはすでに回答にあるとおり可能。
シングルコアCPUでは「マルチタスク」とは言っても「同時」ではなく短い時間で切り替えながら処理しているため「見かけ上」同時のように見えているだけ。
「完全な並列化」を行うためには、CPUが複数必要(マルチコアCPU)。
(ちなみにハイパースレッディングは、マルチコアよりの技術)

但し、
>並列処理はfor文などの単調な処理を並列処理にすることで実行を早くするということがわかったのですが、
コレは理解が不十分。
並列化できるのは「単調な処理」ではなく、「相互に依存関係がない処理」。
複雑な処理でも影響を及ぼさなければ一切問題は発生しない。

たとえば、ある動作を行うために
入力を「モジュールA」で処理し、その結果を「モジュールB」に渡す必要がある場合は並列化はできない。
入力1を「モジュールX」で処理し、入力2を「モジュールY」で処理する。この二つの処理結果を「モジュールZ」で利用するような場合なら、
「モジュールX」と「モジュールY」には依存関係がないため並列化は可能。
    • good
    • 0

No.2です。


ゴメンなさい。質問の趣旨と違っていました。
    • good
    • 0

やり方としては「マルチタスク」による方法と「スレッド」による方法があります。


スレッドは、サブルーチンを呼び出すようにして用います。
マルチタスクは、もう1つのプロセスとして自分自身を呼び出すのですが、わかってしまえばやり方は1通りなのでさほど難しくありません。

UNIX(Linux)の場合は、スレッドでもマルチタスクでもどちらでやっても処理速度はさほど変わらないため、マルチタスクでやるのが一般的です。
Windowsは、スレッドでやるのが一般的です。
ちなみに、マルチタスクの関数は、fork()ですので、これで検索すればいいかと思います。
スレッドの関数は忘れました。あしからず。
    • good
    • 0

可能か不可能か、で言えば「可能」です。



やり方は、コンパイラ、CPU、OS等によって違うので、簡単には説明できません。

また、並列処理することで、逐次処理では起らない問題が発生することがあります。(デッドロック、ライブロック等)
これも、まともに説明すると本一冊書ける内容なので省略します。

forループを並列処理する簡単な方法は、OpenMP対応コンパイラを使用して、#pragma omp parallel for を使用することかな、と思います。
詳しくは OpenMP で検索してください
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!