再入可能性の問題を理解する

昔、再入可能性の問題について記事を書きました

実際に筆記試験を受けている場合、面接官は
次のコードの何が問題なのかと尋ねます。

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

int NUM = 0;

void * PrintNum(void * ptr)
{
    while(NUM++ < 200000000) {
    }
    printf("%s,NUM:%d\n", (char*)ptr, NUM);
    NUM = 0;
}

int main(int argc,char ** argv)
{
    pthread_t thread1 = -1;
    char thread1_name[] = "thread2";
    if(pthread_create(&thread1, NULL, PrintNum, thread1_name)!=0) {
        printf("thread1 creat error\n");
    }

    pthread_t thread2 = -1;
    char thread2_name[] = "thread2";
    if(pthread_create(&thread1, NULL, PrintNum, thread2_name)!=0) {
        printf("thread1 creat error\n");
    }

    void * result1;
    void * result2;
    pthread_join(thread1, &result1);
    pthread_join(thread2, &result2);
}

このコードには次の問題があります。

2 番目のスレッドを作成するときに、間違った変数名が使用されました。

pthread_create(& thread1 , NULL, PrintNum, thread2_name) は 
pthread_create(&
thread2 , NULL, PrintNum, thread2_name)に変更する必要があります。

PrintNum 関数では、グローバル変数 NUM へのアクセスがスレッドセーフであることを保証するために、同期メカニズムは使用されません。複数のスレッドが NUM に同時にアクセスすると、データの不整合やプログラム エラーが発生する可能性があります。スレッドの安全性を確保するには、ミューテックスなどの同期メカニズムを使用する必要があります。

PrintNum 関数では、スレッドの実行時間をシミュレートするために無限ループが使用されます。これにより、スレッドが常に CPU リソースを占有し、システムのパフォーマンスに影響を与えます。一定時間待機し、スレッドをブロック状態にして、CPU リソースを解放するには、sleep 関数を使用する必要があります。

main関数では、スレッド作成・待ち合わせの戻り値をチェックしていないため、スレッド作成・待ち合わせに失敗する場合がありますので、戻り値を確認し、エラー処理を行ってください。

メイン関数では、スレッド リソースは解放されません。
スレッドの実行が完了した後、pthread_exit 関数または pthread_cancel 関数を呼び出して、スレッド リソースを解放する必要があります。

main 関数では、return ステートメントを使用してプログラムを終了するのではなく、プログラムの実行後に return ステートメントを使用してプログラムを終了する必要があります。

上記の回答は、chatgpt にコードを投げたときに得た応答です。

とはいえ、このコードを正しく実行するには、いくつかの変更が必要です。

コードを変更する

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

unsigned long int NUM = 0;

void * PrintNum(void * ptr)
{
    unsigned long int count = 0;
    while(NUM++ < 20000) {
      count++;
    }
    printf("%s run %lu in NUM:%lu \n", (char*)ptr, count, NUM);
    NUM = 0;
    pthread_exit(NULL);
}

int main(int argc,char ** argv)
{
    pthread_t thread1 = -1;
    char thread1_name[] = "thread2";
    if(pthread_create(&thread1, NULL, PrintNum, thread1_name)!=0) {
        printf("thread1 creat error\n");
    }

    pthread_t thread2 = -1;
    char thread2_name[] = "thread2";
    if(pthread_create(&thread2, NULL, PrintNum, thread2_name)!=0) {
        printf("thread1 creat error\n");
    }

    void * result1;
    void * result2;
    pthread_join(thread1, &result1);
    pthread_join(thread2, &result2);

    return 0;
}

実行関数の出力

thread2 .....run 18188 in NUM:20001 
thread2 .....run 6812 in NUM:20001

計算では、2 つのスレッドによるグローバル変数の実行回数が異なることがわかります。

それから私はあらゆる種類のいじりをしました

最後まで、当初のアイデアを完成させなかった

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

unsigned long int g_num = 0;
unsigned long int count_thread1 = 0, count_thread2 = 0;
#define RUN_COUNT 2000
pthread_mutex_t mutex;
pthread_cond_t cond;
int cond_flag = 1;

void * print_num(void * ptr)
{
    while(1) {
        pthread_mutex_lock(&mutex);
        if (g_num >= RUN_COUNT) {
            pthread_mutex_unlock(&mutex);
            break;
        }
        if (strcmp((char*)ptr, "thread1") == 0) {
            if (cond_flag != 1) {
              pthread_cond_wait(&cond, &mutex);
            }
        } else if (strcmp((char*)ptr, "thread2") == 0) {
            if (cond_flag != 2) {
              pthread_cond_wait(&cond, &mutex);
            }
        }
        if (strcmp((char*)ptr, "thread1") == 0) {
            count_thread1++;
            cond_flag = 2;
        } else if (strcmp((char*)ptr, "thread2") == 0) {
            count_thread2++;
            cond_flag = 1;
        }
        g_num++;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
    if (strcmp((char*)ptr, "thread1") == 0) {
        printf("%s run %lu in g_num:%lu \n", (char*)ptr, count_thread1, g_num);
    } else if (strcmp((char*)ptr, "thread2") == 0) {
        printf("%s run %lu in g_num:%lu \n", (char*)ptr, count_thread2, g_num);
    }
    g_num = 0;
    printf("%s run over \n", (char*)ptr);
    pthread_exit(NULL);
}
int main(int argc,char ** argv)
{
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    pthread_t thread1 = -1;
    char thread1_name[] = "thread1";
    if(pthread_create(&thread1, NULL, print_num, thread1_name)!=0) {
        printf("thread1 creat errorn");
        return -1;
    }
    pthread_t thread2 = -1;
    char thread2_name[] = "thread2";
    if(pthread_create(&thread2, NULL, print_num, thread2_name)!=0) {
        printf("thread1 creat errorn");
        return -1;
    }
    while (1) {
        if (g_num > RUN_COUNT) {
          if (count_thread1 < RUN_COUNT/2) {
              cond_flag = 1;
              pthread_cond_signal(&cond);
          } else if (count_thread2 < RUN_COUNT/2) {
              cond_flag = 2;
              pthread_cond_signal(&cond);
          } else {
            break;
          }
        }
    }
    void * result1;
    void * result2;
    pthread_join(thread1, &result1);
    pthread_join(thread2, &result2);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

コード出力

thread2 run 1000 in g_num:2000 
thread2 run over 
thread1 run 1001 in g_num:2001 
thread1 run over

上記の 2 つのコード セクションでは、最初のセクションはクリティカル セクションを保護していません.2 つのスレッドがスケジュールされている場合、クリティカル セクションへのアクセスもランダムになります。

2 番目の段落では、条件変数とロック保護を追加して、プロセス全体を定期的に実行できるようにし、ソフトウェアのロジックを保証します。

条件変数を追加せずにロック保護のみを追加すると、クリティカル セクションの 1 つのタイム スライスに 1 つのスレッド アクセスしか保証されず、クリティカル セクションへのアクセスの順序は保証されません。

C++ での条件変数の使用については、これらのリンクを参照してください。

https://en.cppreference.com/w/cpp/thread/condition_variable

https://stackoverflow.com/questions/73543664/do-i-need-to-lock-the-mutex-before-calling-condition-variablenotify

https://www.zhihu.com/question/541037047

44b29ca36fcdbb203e848fdb96920777.png

0e80c7eeeb95a863d53fd798ade7350b.jpeg

7348a3d69ee5081a0df3dcc47b57e28b.jpeg

おすすめ

転載: blog.csdn.net/weiqifa0/article/details/130256502