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

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

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

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

Cygwin

Cygwinは、Unixのような環境を、Windows上で構築させるコマンドラインインターフェースです。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

コンパイラ

コンパイラは、プログラミング言語で記述したソースコードを、コンピュータの実行形式であるオブジェクトコードに変換するプログラムです。

Q&A

解決済

3回答

4437閲覧

実行環境の違いによるスレッドの動作の違いについて

johejo

総合スコア12

C

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

Cygwin

Cygwinは、Unixのような環境を、Windows上で構築させるコマンドラインインターフェースです。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

コンパイラ

コンパイラは、プログラミング言語で記述したソースコードを、コンピュータの実行形式であるオブジェクトコードに変換するプログラムです。

0グッド

0クリップ

投稿2016/12/22 16:25

編集2016/12/23 04:28

###前提
C言語でスレッドの勉強をしています。
練習がてら次のようなプログラムを作成しました。

コマンドライン引数で合計カウント数とスレッド数を入力し実行すると、スレッドごとに0からカウントを始めてすべてのスレッドのカウントの総和が入力した合計カウント数に達すると、プログラムの実行にかかった時間を表示するプログラムです。
標準入出力にスレッド番号とそのスレッドが現在カウントできている値を表示させるようにしています。

スレッド数を増やすと動作がどのように変化するのか、また実行環境を変えると動作が変化するのかを確かめたくてこのようなプログラムを作成してみました。
###発生している問題
プログラム自体はおそらく完成しているのですが、実行環境のせいかMacの標準のターミナル上ではうまく動作するものの、WindowsのCygwinターミナル上ではうまく動作しません。

デバッグしてみたところ、どうやら私のCygwin上ではスレッド数が4を超えると5つめからのスレッドのカウントが私の想像通りには始まっていないことはわかりました。

MacのXcodeに付属するCコンパイラでコンパイルして、ターミナルで実行すると想像通りの動作をしました。

勉強中の拙いソースコードですが、なぜこのようなことが起こるのか教えていただけないでしょうか。

#実行例について
スレッド数4で実行した場合
$./a.out 100 4
begin 100 4
Thread0 1
Thread1 1
Thread2 1
Thread3 1
Thread3 2
Thread1 2
Thread2 2
Thread0 2
Thread1 3
Thread3 3
Thread2 3
Thread0 3
===中略===
Thread3 24
Thread1 24
Thread2 24
Thread0 24
Thread3 25
Thread1 25
Thread0 25
Thread2 25
sum 100
12.109[sec]

スレッド数5で実行した場合
$./a.out 100 5
begin 100 5
Thread0 1
Thread1 1
Thread2 1
Thread3 1
Thread1 2
Thread3 2
Thread0 2
Thread2 2
Thread1 3
Thread3 3
Thread0 3
Thread2 3
===中略===
Thread1 95
Thread3 95
Thread2 96
Thread0 97
Thread1 96
Thread3 96
Thread2 97
Thread0 98
Thread1 97
Thread4 1
Thread3 97
Thread2 98
sum 391
38.390[sec]

###該当のソースコード

C

1#include <stdio.h> 2#include <time.h> 3#include <stdlib.h> 4#include <string.h> 5#include <pthread.h> 6 7static pthread_mutex_t MyMutex=PTHREAD_MUTEX_INITIALIZER; 8 9void *thread_func(void *threadArgs); 10 11struct ThreadArgs 12{ 13 int *count_num; 14 int *n; 15 int *sum; 16 int thread_id; 17}; 18 19void *thread_func(void *threadArgs) { 20 int i, thread_id; 21 int *n, *count_num, *sum; 22 23 pthread_detach(pthread_self()); 24 25 n = ((struct ThreadArgs *) threadArgs) -> n; 26 sum = ((struct ThreadArgs *) threadArgs) -> sum; 27 count_num = ((struct ThreadArgs *) threadArgs) -> count_num; 28 thread_id = ((struct ThreadArgs *) threadArgs) -> thread_id; 29 30 while ( *sum < *count_num) { 31 (*n)++; 32 printf("Thread%d %d\n", thread_id, *n); 33 for (i = 0; i < 50000000; i++) {} 34 } 35 36 free(threadArgs); 37} 38 39int main(int argc, char *argv[]) 40{ 41 int i, j, *n, *top, count_num, thread_num, sum; 42 clock_t time1, time0; 43 double diff; 44 struct ThreadArgs *threadArgs; 45 pthread_t mythread; 46 int thread_id; 47 48 if (argc != 3) { 49 exit(1); 50 } 51 52 count_num = atoi(argv[1]); 53 thread_num = atoi(argv[2]); 54 55 printf("begin %d %d\n", count_num, thread_num); 56 57 n = (int *)malloc(sizeof(int) * thread_num); 58 59 top = n; 60 61 for (i = 0, n = top; i < thread_num; i++, n++) { 62 *n = 0; 63 } 64 65 sum = 0; 66 67 time0 = clock(); 68 69 thread_id = 0; 70 71 for (i = 0, n = top; i < thread_num; i++, n++) { 72 if((threadArgs = (struct ThreadArgs *)malloc(sizeof(struct ThreadArgs))) == NULL){ 73 fprintf(stderr, "malloc failed¥n"), exit(1); 74 } 75 76 threadArgs -> count_num = &count_num; 77 threadArgs -> n = n; 78 threadArgs -> sum = &sum; 79 threadArgs -> thread_id = thread_id; 80 81 if(pthread_create(&mythread, NULL, thread_func, (void *) threadArgs) != 0){ 82 fprintf(stderr, "pthread_create() failed¥n"), exit(1); 83 } 84 85 thread_id++; 86 } 87 88 while (sum < count_num) { 89 pthread_mutex_lock(&MyMutex); 90 for (i = 0, sum = 0, n = top; i < thread_num; i++, n++) { 91 sum += *n; 92 } 93 pthread_mutex_unlock(&MyMutex); 94 //printf("%d %d\n", sum, count_num); 95 } 96 printf("sum %d\n", sum); 97 98 time1 = clock(); 99 100 diff =(double)(time1 - time0) / CLOCKS_PER_SEC; 101 102 printf("%.3f[sec]\n", diff); 103 104 free(top); 105 106 return 0; 107} 108 109

##追加の質問内容
なぜスレッド数5の実行例では、Thread 4(5つめのスレッド)がすぐに開始されないのでしょうか。
##追記
排他処理を追加しました。
追加の質問内容と実行例について加筆しました。

###補足情報(言語/FW/ツール等のバージョンなど)
実行環境
Windows10 Home (intel Core i5 4460)
Cygwin (gnupack_devel-13.06-2015.11.08)
GCC 4.9.3

macOS Sierra 10.12.1 (Core2Duo SU9400)

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

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

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

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

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

KSwordOfHaste

2016/12/22 23:58

「スレッドのカウントが私の想像通りには始まっていない」点が何かをより具体的に述べるか少し出力例を示して説明すると閲覧者の人たちにわかりやすくなると思います。
yohhoy

2016/12/23 02:27 編集

あなたが期待する動作/実行結果を明記下さい。(プログラムには排他制御の不足によるデータ競合と、そもそもの設計からして決定的動作=同じ結果がえられる保証が無い問題の2つがあるように見えます)
guest

回答3

0

ベストアンサー

mainを実行しているメインスレッドになかなか実行権が渡されないので、 while (sum < count_num) がなかなか処理されないまま、子スレッドが処理し続けている・あるいは既に実行を終了していることが考えられます。スレッドへ実行権を与える・剥奪する処理は、OSに強く依存し、そのOSが独自のスケジューリング・アルゴリズムを用いて、ハードウェア構成も含めたシステム全体の都合によってなされます。したがって、スレッドの実行順序は全く保障されません。「想像通りに」動く方が不思議で、特にシステム負荷が重い場合は複雑な挙動をします。

よってマルチスレッドプログラミングでは、スケジューリングに依存しない書き方が必要で、どうしても実行順序を決めたいときはセマフォなど「同期オブジェクト」を用いてスレッド同士の「待ち合わせ」を行うことが通例です。

追加

セマフォ2つを使った同期を示します。
そこそこまともに動きます。

C

1#include <stdio.h> 2#include <time.h> 3#include <stdlib.h> 4#include <string.h> 5#include <pthread.h> 6#include <semaphore.h> 7 8struct ThreadArgs 9{ 10 int *count_num; 11 int *n; 12 int *sum; 13 int thread_id; 14 sem_t* sem4n; 15 sem_t* sem4sum; 16}; 17 18void *thread_func(void *threadArgs); 19 20void *thread_func(void *threadArgs) { 21 int i, thread_id; 22 int *n, *count_num, *sum; 23 sem_t* sem4n; 24 sem_t* sem4sum; 25 26 pthread_detach(pthread_self()); 27 28 struct ThreadArgs *args = (struct ThreadArgs *) threadArgs; 29 30 n = args -> n; 31 sum = args -> sum; 32 count_num = args -> count_num; 33 thread_id = args -> thread_id; 34 sem4n = args -> sem4n; 35 sem4sum = args -> sem4sum; 36 37 free(threadArgs); 38 39 while ( *sum < *count_num) { 40 (*n)++; 41 printf("Thread%d %d\n", thread_id, *n); 42 43 /* 共有データ*nを使用したので、セマフォの加算 */ 44 sem_post(sem4n); 45 46 for (i = 0; i < 50000000; i++) {} 47 48 /* 共有データ*sum, *count_numが使えるようになるまで、セマフォの値によるブロック */ 49 sem_wait(sem4sum); 50 } 51} 52 53int main(int argc, char *argv[]) 54{ 55 int i, j, *n, *top, count_num, thread_num, sum; 56 clock_t time1, time0; 57 double diff; 58 struct ThreadArgs *threadArgs; 59 pthread_t mythread; 60 int thread_id; 61 static sem_t sem4n; 62 static sem_t sem4sum; 63 64 if (argc != 3) { 65 exit(1); 66 } 67 68 count_num = atoi(argv[1]); 69 thread_num = atoi(argv[2]); 70 71 printf("begin %d %d\n", count_num, thread_num); 72 73 n = (int *)malloc(sizeof(int) * thread_num); 74 75 /* セマフォの初期化 */ 76 if (sem_init( &sem4n, 0, 0) != 0) { 77 fprintf(stderr, "sem_init() failed\n"); 78 exit(1); 79 } 80 if (sem_init( &sem4sum, 0, 0) != 0) { 81 fprintf(stderr, "sem_init() failed\n"); 82 exit(1); 83 } 84 85 top = n; 86 87 for (i = 0, n = top; i < thread_num; i++, n++) { 88 *n = 0; 89 } 90 91 sum = 0; 92 93 time0 = clock(); 94 95 thread_id = 0; 96 97 for (i = 0, n = top; i < thread_num; i++, n++) { 98 if((threadArgs = (struct ThreadArgs *)malloc(sizeof(struct ThreadArgs))) == NULL){ 99 fprintf(stderr, "malloc failed?n"), exit(1); 100 } 101 102 threadArgs -> count_num = &count_num; 103 threadArgs -> n = n; 104 threadArgs -> sum = &sum; 105 threadArgs -> thread_id = thread_id; 106 threadArgs -> sem4n = &sem4n; 107 threadArgs -> sem4sum = &sem4sum; 108 109 if(pthread_create(&mythread, NULL, thread_func, (void *) threadArgs) != 0){ 110 fprintf(stderr, "pthread_create() failed\n"), exit(1); 111 } 112 113 thread_id++; 114 } 115 116 while (sum < count_num) { 117 for (i = 0, sum = 0, n = top; i < thread_num; i++, n++) { 118 /* 共有データ*nが使えるようになるまで、セマフォの値によるブロック */ 119 sem_wait(&sem4n); 120 sum += *n; 121 122 /* 共有データsum, count_numを使用したので、セマフォの加算 */ 123 sem_post(&sem4sum); 124 } 125 //printf("%d %d\n", sum, count_num); 126 } 127 128 /* セマフォの削除 */ 129 sem_destroy(&sem4n); 130 sem_destroy(&sem4sum); 131 132 printf("sum %d\n", sum); 133 134 time1 = clock(); 135 136 diff =(double)(time1 - time0) / CLOCKS_PER_SEC; 137 138 printf("%.3f[sec]\n", diff); 139 140 return 0; 141}

begin 1000 20

Thread0 1
Thread1 1
Thread2 1
Thread3 1
Thread4 1
Thread5 1
Thread6 1
Thread7 1
Thread8 1
Thread9 1
Thread10 1
Thread11 1
Thread12 1
Thread13 1
Thread14 1
Thread15 1
Thread16 1
Thread17 1
Thread18 1
Thread19 1
Thread0 2
Thread2 2
Thread5 2
Thread1 2
Thread3 2
Thread6 2
Thread4 2
Thread7 2
Thread9 2
Thread8 2
Thread10 2
Thread11 2
Thread12 2
Thread14 2
Thread13 2
Thread15 2
Thread16 2
Thread17 2
Thread18 2
Thread2 3
Thread6 3
Thread10 3
Thread5 3
Thread0 3
===中略===
Thread5 61
Thread1 61
Thread9 61
Thread17 40
Thread13 61
Thread0 52
Thread4 52
Thread8 52
Thread12 52
Thread2 54
Thread16 52
Thread18 51
Thread6 54
Thread5 62
Thread10 54
Thread1 62
Thread14 54
Thread9 62
Thread13 62
Thread19 40
Thread0 53
Thread3 41
Thread4 53
Thread7 41
Thread8 53
sum 1010
128.482[sec]

環境
Windows 7 Professional SP1
Intel Core i5 750
Cygwin64
GCC 5.4.0

投稿2016/12/23 03:49

編集2016/12/23 11:35
naomi3

総合スコア1105

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

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

johejo

2016/12/23 14:20

サンプルコードまで本当にありがとうございました。 大学の授業で感じた疑問をプログラムにしてみたところ、動作に対する更なる疑問を生じさせてしまいましたが、解決できてよかったと思います。 マルチスレッドの場合は単にプログラムを書くだけでなく予期しない動作が起こりうることも考えながら、注意しようと思います。 他の解答者の方々もありがとうございました。
guest

0

スレッドに対するCPUの割り当てが異なる事により、発生している様です。
5スレッドの場合に、CPUの割り当てが大きく変化している様に見られます。
実際に正しいものの中でも、各スレッドが起動順に結果が現れない場合が見れます。ただ五千万回のwhileループが行われている内に追いついている様子です。
Linux環境でもスレッド数を多くするとスレッド間で遅れが発生して同様な現象が起こります。

投稿2016/12/23 03:42

編集2016/12/23 07:13
A.Ichi

総合スコア4070

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

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

0

struct ThreadArgsのメンバにはthread_id以外はポインタで、それはすべてのスレッドで同じオブジェクト(特にsum)を参照しているので不可解な動作になる、と私は想像します。Xcodeでも最適化をONにしてビルドすれば、cygwinと似た挙動になるのではないでしょうか。

複数(メインスレッド含)のスレッドが同じ資源を扱う時には「排他制御」が必要です。

投稿2016/12/23 01:32

編集2016/12/23 01:35
sharow

総合スコア1149

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問