0

プロセッサの負荷を発生させる簡単なテスト プログラムを作成します。6 つのスレッドをスローし、すべてのスレッドで pi を計算します。しかし、プロセッサはターゲット プラットフォーム (arm) で 3 つのスレッドしか生成しません。通常の Linux-PC で同じプログラムを実行すると、6 つのスレッドすべてが生成されます。

何が問題ですか?

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define ITERATIONS 10000000000000
#define NUM_THREADS 6

void *calculate_pi(void *threadID) {
        double i;
        double pi;
        int add = 0;

        pi = 4;
        for (i = 0; i < ITERATIONS; i++) {
                if (add == 1) {
                        pi = pi + (4/(3+i*2));
                        add = 0;
                } else {
                        pi = pi - (4/(3+i*2));
                        add = 1;
                }
        }

        printf("pi from thread %d = %20lf in %20lf iterations\n", (int)threadID, pi, i);

        pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
        pthread_t threads[NUM_THREADS];
        int rc;
        int i;

        for ( i = 0 ; i < NUM_THREADS; i++) {
                rc = pthread_create(&threads[i], NULL, calculate_pi, (void *)i);
                if (rc) {
                        printf("ERROR; return code from pthread_create() is %d\n", rc);
                        exit(EXIT_FAILURE);
                }
        }

        for ( i = 0 ; i < NUM_THREADS; i++) {
                pthread_join(threads[i], NULL);
        }

        return(EXIT_SUCCESS);
}
4

4 に答える 4

1

オペレーティング システムが、pthread_create でスレッドを生成するのと同じ数のカーネル レベルのスレッド/タスクを作成することを保証するものは何もありません。ユーザーランドですべてを行い、1 つのカーネルレベルのスレッドと CPU のみを使用する pthreads 実装があります。多くの (ほとんどの?) 実装では、実装が最も簡単なため、1 つのスレッドが 1 つのカーネル レベルのスレッドである 1:1 スレッド化を行います。ユーザーランド ライブラリが生成するカーネル レベルのスレッド数を決定する M:N ハイブリッド モデルを実装するものもあります。これは、使用する実装に当てはまる場合があります。「ps -eLF」は、カーネル レベルのスレッドのみを表示します。ユーザー レベルのスレッドに関する情報はありません。

M:N スレッド化の利点は、さまざまなユーザー レベル スレッド間のコンテキストの切り替えが、場合によっては非常に高速になることです。欠点は、実装がはるかに複雑であり、通常、実装が非常に脆弱であることです。

于 2012-10-22T09:02:52.810 に答える
1

プロセッサをロードすることが目的であり、OpenMP をサポートするコンパイラがある場合は、次を使用できます。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <omp.h>

double calculate_pi(int iterations) {

  double pi;
  int add = 0;

  pi = 4;
  for (int ii = 0; ii < iterations; ii++) {
    if (add == 1) {
      pi = pi + (4.0/(3.0+ii*2));
      add = 0;
    } else {
      pi = pi - (4.0/(3.0+ii*2));
      add = 1;
    }
  }
  return pi;
}

int main(int argc, char *argv[]) {

  if ( argc != 2 ) {
    printf("Usage: %s <niter>",argv[0]);
    return 1;
  }
  const int iterations = atoi(argv[1]);

#pragma omp parallel
  {
    double pi = calculate_pi(iterations);
    printf("Thread %d, pi = %g\n",omp_get_thread_num(),pi);
  }
  return 0;
}

このようにして、コマンドラインから反復回数を設定し、環境変数からスレッド数を設定できますOMP_NUM_THREADS。例えば:

export OMP_NUM_THREADS=4
./pi.x 1000

実行可能ファイルを 1000 回の繰り返しと 4 つのスレッドで実行します。

于 2012-10-22T08:22:39.917 に答える
1

メインスレッドが新しいスレッドを作成すると、所有している CPU の数やその他の要因に応じて、ライブラリ/OS はすぐに新しいスレッドに切り替えて、ブロックまたは終了するまでその新しいスレッドを実行することを決定できます。次に、ブロックまたは終了するまで実行される別の新しいスレッドを作成するメインスレッドに戻ります。この場合、実際に同時に実行されるスレッドが 2 つを超えることはありません (メイン スレッドと新しいスレッドの 1 つ)。

もちろん、CPU の数が多いほど、新しいスレッドをすべて生成するのに十分な時間、メイン スレッドが実行し続ける可能性が高くなります。これが起こったことだと思います.あなたのPCは単にARMシステムよりも多くのCPUを持っています.

これを防ぐ最善の方法は、新しいスレッドの優先度をメイン スレッドよりも低くすることです。そうすれば、優先度の高いメイン スレッドが優先度の低いスレッドを作成するときに、ライブラリ/カーネルは、優先度の高いスレッドの実行を停止しないように十分にスマートにする必要があります。

悲しいことに、Linux での pthread の実装には、通常の pthread スレッドの優先順位を無視する習慣があります。私が最後に調べたとき、唯一の代替手段は代わりにリアルタイムのスレッド優先度を使用することでした.これにはルートアクセスが必要であり、セキュリティ/権限の災害を引き起こします. これは、カーネル内の基礎となるスケジューラーの制限が原因である可能性があります (たとえば、pthreads ライブラリーが回避できない問題)。

別の選択肢があります。メインスレッドが新しいスレッドを作成する前にミューテックスを取得し、すべての新しいスレッドが作成された後にそれを解放した場合、および他のスレッドが実際の作業を行う前に同じミューテックスを取得 (および解放) しようとした場合。次に、7 つのスレッドすべてを同時に持つように強制します。

于 2012-10-22T10:24:25.880 に答える
0

おそらく、1000 秒 (スリープ時) では、多くの反復を終了するには不十分です。そのため、プログラムは 6 つのスレッドが完了する前に終了する可能性があります。

寝る代わりに参加してみましたか?

これのために sleep() を置き換えてみてください:

for ( i = 0 ; i < NUM_THREADS; i++) {
    s = pthread_join(threads[i], NULL);
}
于 2012-10-22T08:04:08.353 に答える