質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

4回答

1729閲覧

C言語での並列処理で総和の計算が一致しない。

Savanof

総合スコア33

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2021/06/16 05:31

編集2021/06/16 06:43

前提・実現したいこと

C言語において並列処理(pthread.h)を用いて0からnまでの総和を求めるプログラムの計算を行いたいと考えています。

該当のソースコード

ワークフローとしては

1. DATA_NUMで0から幾つまでの数の総和を求めるのか決め、DATANUMの値を基にそのサイズのdata二次元配列を作成する。 2. data[i][0]にiを代入、data[i][1]にdata[i][0]が計算済みか否かのフラグを導入 3. 並列処理部分の関数(do_calc)で並列処理を行い、data[i][0]の値をグローバル変数のsumに値を追加 , [i][1]に処理を行ったスレッドの番号を追加 4. main関数でsumをprintfし、各スレッドがsumに何回加算したか計算する。

C

1#include <pthread.h> 2#include <stdio.h> 3#include <stdlib.h> 4#include <unistd.h> 5 6 7// 幾つのスレッドで並列処理を行うか 8#define THREAD_NUM 4 9 10// いくつまでの数の総和を求めるか 11#define DATA_NUM 100000 12 13// 結果を収納する配列 14unsigned long data[DATA_NUM][2]; 15 16// 総和を収納する配列 17unsigned long sum = 0; 18 19//Mutexオブジェクト 20pthread_mutex_t mutex; 21 22 23pthread_t tid[THREAD_NUM]; // スレッドID を保持する配列 24int param[THREAD_NUM]; 25 26 27// プロトタイプ処理 28void *do_calc(void *); 29 30 31void *do_calc(void* arg) { 32 33 // 1回の処理で担当する回数 34 int Number_Interval; 35 Number_Interval = DATA_NUM/THREAD_NUM; 36 37 //StartするIndex 38 int Start_Index , *id; 39 id = arg; 40 Start_Index = *id * Number_Interval; 41 42 //終わるIndex 43 int Finish_Index; 44 Finish_Index = Start_Index + Number_Interval; 45 46 for(int i = Start_Index;i<Finish_Index;i++){ 47 48 // もし値が0(未作業)の場合は、data[i][0]の値をグローバル変数のsumに追加した後にdata[i][1]にスレッド番号を収納 49 if (data[i][1] == 0){ 50 pthread_mutex_lock(&mutex); 51 sum = sum + data[i][0]; 52 data[i][1] = arg; 53 pthread_mutex_unlock(&mutex); 54 } 55 56 } 57 pthread_exit(NULL); 58 59 60} 61 62 63int main(){ 64 65 // DATA_NUM-1の範囲で、data[i][0]にはiを代入 、 data[i][1]には0を代入 66 for(int i = 0; i <DATA_NUM;i++){ 67 data[i][0] = i; 68 data[i][1] = 0; 69 } 70 71 //Mutexオブジェクトの初期化 72 pthread_mutex_init(&mutex, NULL); 73 74 75 //指定された数のスレッドを作成する。 76 for (int i =0; i < THREAD_NUM; i++){ 77 param[i] = i; 78 pthread_create(&tid[i],NULL,do_calc,&param[i]); 79 } 80 81 82 for(int i=0; i<THREAD_NUM; i++){ 83 pthread_join(tid[i], NULL); 84 } 85 86 for(int i=0; i < DATA_NUM; i++){ 87 printf("%lu \n",data[i][1]); 88 89 90 } 91 92 printf("Sum is %lu \n",sum); 93 94 95 96} 97 98 99 100//i=0からdata[i][1]を調べて、それが0だったら、「作業」が済んでいないとみなして、data[i][0]の値をグローバル変数sumに加算する。 101 102//「作業済み」として、data[i][1]にスレッドを区別する番号(例えば、1からTHREAD_NUMのどれか)を代入して次のiを調べる。 103 104//iが DATA_NUM-1まで調べたら各スレッドは終了する。 105 106//すべてのスレッドが終了したら、mainの中で、sumの値と、各スレッドがsumに何回加算したかを表示する。 107 108//各スレッドがsumに何回加算したかは、data[i][1]を全部調べて、その値を表示してください。 109 110//sumを正しく計算するために、グローバル変数sumへの加算時と、data[i][1]で作業済みかどうかのチェックなどで必要な排他制御を行うプログラムにしてください。 111//DATA_NUMが100の場合、0から99の和ですから、4950が答えになるはずです。

発生している問題・エラーメッセージ

大方の実装は終わっているのですが、DATA_NUMの値が100000を超えた時の総和がおかしくなる、ワークフロー4.の「各スレッドがsumに何回加算したか計算する」の実装を行うにあたってスレッド番号が大きくなってしまう(実際の0,1,2,3...)というエラーが解決できていないです。

DATA_NUM = 100 THREAD_NUM = 4 の時 4212384 ... 4212388 .... 4212392 ... 4212396 ------------- Sum is 4950 -------------
DATA_NUM = 1000 THREAD_NUM = 4 の時 ------------- Sum is 4995000 ------------- DATA_NUM = 10000 THREAD_NUM = 4 の時 ------------- Sum is 4995000 ------------- DATA_NUM = 100000 THREAD_NUM = 4 の時 ------------- Sum is 5 -------------

補足情報(FW/ツールのバージョンなど)

paiza ioでコンパイルしました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kaina

2021/06/16 06:16

>DATA_NUMの値が100000を超えた時の総和がおかしくなる とありますが、別にDATA_NUMの値が100000以外も全部間違った総和ですよ? まずTHREAD_NUMを1、DATA_NUMを10辺りにしてデバッグしたほうが良いかと思います。
guest

回答4

0

ベストアンサー

sum = sum + data[i][0];

グローバル変数sumへの操作がアトミック操作ではないためsumが不定になります。

この場合はそれぞれのスレッドの範囲内の合計を計算した後にpthread_exit,pthread_joinでメインスレッドに戻した後に合計してやるとよいと思います。
こんな感じ

スレッド番号が大きくなってしまう

については、argがポインタだからです。

c

1int id = *(int*)arg;

みたいにしないといけません。

投稿2021/06/16 06:56

asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

asm

2021/06/16 06:59 編集

今回の場合変数sumに対し排他制御するとスレッドを使う意味がなくなると思っています。
Savanof

2021/06/16 06:59

リンク付きでの解説ありがとうございます、キーワードが分かったので後は自決できそうです。
guest

0

sum = sum + data[i][0];

を実行するとき、コンピュータ(つまりCPU)は
(1)sumという変数(で表されるアドレスのメモリ)をレジスタに持ってきて、
(2)それにdata[i][0]を加えてから、
(3)もとのメモリに書き戻します。

スレッド並列を行っていると、以下の順番に処理が起こる可能性があります。

スレッドA(1)sumという変数(で表されるアドレスのメモリ)をレジスタに持ってくる。
スレッドB(1)sumという変数(で表されるアドレスのメモリ)をレジスタに持ってくる。
スレッドA(2)それにdata[iA][0]を加える。
スレッドB(2)それにdata[iB][0]を加える。
スレッドA(3)もとのメモリに書き戻す。
スレッドB(3)もとのメモリに書き戻す。

これが起こると、data[iA][0]を加えた結果は一旦sumに保管されますが、その直後にスレッドBの書込みによって失われます。

従って、スレッド並列で同じメモリ領域(変数)に書き込むと、結果が正しくないことは良く起こります。
これを避けるためには、akiruno-oneoneさんが書いているように排他処理が必要です。

投稿2021/06/16 06:47

ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Savanof

2021/06/16 06:50

返信ありがとうございます。mutexを使用した排他処理をおこなって同時にsumにアクセスできないような処理を加えたのですが、未だに一部の結果がおかしいです。mutexの使い方がおかしかったりしますか?
guest

0

スレッド間で共有されるデータにアクセスする場合は、排他処理が必要です。
ぱっとみたところ、計算結果を格納するsumはスレッド間で排他処理がされていません。
mutexなどで排他処理を行いましょう。

投稿2021/06/16 06:22

akiruno-oneone

総合スコア815

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Savanof

2021/06/16 06:42

mutexで排他処理を行ってみたのですが、結果変わらず、値が同じままでした... コードを更新したので、もし可能であればmutexの使い方を教えて頂けませんか?
guest

0

(ふと見直したら適切なサイズになっていました。私の質問のコードの見間違えでした.... )

排他制御についてみんな書かれているので、そもそもなんですが、int型のサイズを越えた計算になっていないか確認した方がいいですね。

intは型c言語だと大抵の場合(処理系依存ですが)、2byteとか4byteだと思います。とりあえず4byteとして、符号付きなので、 -2^31 ~ (2^31-1) [2^nは2のn乗とする]の範囲になります。この正の数の最大値を計算すると、わかりやすいように3桁でコンマを入れて記述します。

2^32 -1
= 2,147,483,647

とります。で、1 ~ (n-1)までの総和は sum(n)=n * (n-1) / 2となるので、

sum(100000) = 100000/2 * (100000 - 1)
= 4,999,950,000

となります。もし4byteのサイズであれば、なのでどこかでint型のサイズが溢れます。

なので、範囲は負数を使わないので、(おそらくですが)unsinged longあたりを使うべきでしょう。(あくまでサイズは処理系依存なので、適切サイズになるようにしてくださいね)

投稿2021/06/16 07:52

編集2021/07/08 06:46
nobkz

総合スコア320

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問