1

私はスレッドにかなり慣れていないので、洞察が欲しいです。各スレッドが計算のために完了したパーセンテージを取得しようとしています。各スレッドは、そのパーセンテージを同じ配列の異なる要素に報告します。私はこれをpthread_join直後に使用pthread_createし、配列のすべての値を読み取ってパーセンテージを出力するための別のスレッドを使用していますが、前のスレッドが終了するのを待たずにすべてのスレッドを次々に実行すると、奇妙な動作が発生します。これは、共有 (グローバル) 配列にアクセスする方法です。

//global
int *currentProgress;
//main
    currentProgress = malloc(sizeof(int)*threads);
    for(i=0; i<threads; i++)
        currentProgress[i] = 0;
//child threads
currentProgress[myId] = (int)percent; //myId is unique

//progress thread
for(i=0; i<threads; i++)
    progressTotal += currentProgress[i];
progressTotal /= threads;
printf("Percent: %d", progressTotal);

これは基本的に、マルチスレッドで正しく使用されていないと私が考えるコードです。共有配列の状態を出力すると、別のスレッドが配列へのアクセスを開始するとすぐに (別の要素ですが)、前の要素がすぐに乱数になることに気付きます...-2147483648そして、後者の要素が前の要素を終了すると、通常どおり継続します。これにはセマフォを使用する必要がありますか? 配列のさまざまな要素に同時にアクセスできると思い、それらを読み取ることは問題ではないと思いました。

これはコード全体です:

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include <pthread.h>
#include <string.h>

#define STDIN 0


int counter = 0;
uint64_t *factors;
void *getFactors(void *arg);
void *deleteThreads(void *arg);
void *displayProgressThread(void *arg);
int *currentProgress;

struct data
{
    uint64_t num;
    uint64_t incrS;
    uint64_t incrF;
    int threads;
    int member;
} *args;

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

    if(argc < 3) {printf("not enough arguments"); exit(1);}

    int i;
    int threads = atoi(argv[2]);
    pthread_t thread_id[threads];
    pthread_t dThread;

    currentProgress = malloc(sizeof(int)*threads);
    for(i=0; i<threads; i++)
        currentProgress[i] = 0;

    args = (struct data*)malloc(sizeof(struct data));
    args->num = atoll(argv[1]);
    args->threads = threads;

    uint64_t increment = (uint64_t)sqrt((uint64_t)args->num)/threads;
    factors = (uint64_t*)malloc(sizeof(uint64_t)*increment*threads);

    pthread_create(&dThread, NULL, displayProgressThread, (void*)args);

    //for the id of each thread
    args->member = 0;
    for(i=0; i<threads; i++)
    {
            args->incrS = (i)*increment +1;
            args->incrF = (i+1)*increment +1;
            pthread_create(&thread_id[i], NULL, getFactors, (void*)args);
            usleep(5);
    }

    for(i=0; i<threads; i++)
    {
        pthread_join(thread_id[i], NULL);
    }
    sleep(1);
    printf("done\n");
    for (i=0; i<counter; i++)
        printf("\n%llu : %llu", factors[++i], factors[i]);
    return 0;
}

void *getFactors(void *arg)
{
    uint64_t  count;
    int myId;
    int tempCounter = 0, i;
    struct data *temp = (struct data *) arg;
    uint64_t number = temp->num;
    float total = temp->incrF - temp->incrS, percent;

    myId = temp->member++;

    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    for(count=temp->incrS; count<=temp->incrF; count++)
    {

        percent = (float)(count-temp->incrS)/total*100;
        currentProgress[myId] = (int)percent;

        if (number%count == 0)
         {
                factors[counter++] = count;
                factors[counter++] = number/count;
         }   
        usleep(1);
    }
    usleep(1);
    pthread_exit(NULL);
}

void *displayProgressThread(void *arg)
{
    struct data *temp = (struct data *) arg;
    int toDelete = 0;
    while(1)
    {
        int i;
        int progressTotal = 0;
        char *percent = malloc(sizeof(char)*20);
        for(i=0; i<toDelete; i++)
            printf("\b \b");

        for(i=0; i<temp->threads; i++){
            progressTotal += currentProgress[i];
        }

        progressTotal /= temp->threads;
        printf("|");
        for(i=0; i<50; i++)
            if(i<progressTotal/2)
                printf("#");
            else
                printf("_");
        printf("| ");
        sprintf(percent, "Percent: %d", progressTotal);
        printf("%s", percent);
        toDelete = 53 + strlen(percent);


usleep(1000);
    fflush(stdout);
    if(progressTotal >= 100)
        pthread_exit(NULL);
}

}

4

1 に答える 1

1

この問題の原因となるスレッドによってアクセスされる同期されていないコードがいくつかあります。

最初に同期する場所は次のとおりです。

   myId = temp->member++;

しかし、もっと重要なことは、メイン スレッドが実行していることです。

   args->incrS = (i)*increment +1;
   args->incrF = (i+1)*increment +1;

同時にスレッド内で:

   for(count=temp->incrS; count<= temp->incrF; count++)
    {

        percent = (float)(count-temp->incrS)/total*100;
        currentProgress[myId] = (int)percent;

        if (number%count == 0)
         {
                factors[counter++] = count;
                factors[counter++] = number/count;
         }   
        usleep(1);
    }

上記の非同期アクセスは、percent値の計算に影響を与え、このような異常な事態を引き起こします。期待どおりの動作を得るには、これらすべての場所で同期を行う必要があります。

于 2012-10-13T09:43:25.717 に答える