Figure out the problem of reentrancy

A long time ago, I wrote an article on reentrancy issues

If you are actually in the written test, the interviewer asks
-what is wrong with the following code?

#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);
}

This code has the following problems:

When creating the second thread, the wrong variable name was used.

pthread_create(& thread1 , NULL, PrintNum, thread2_name) 
should be changed to pthread_create(& thread2 , NULL, PrintNum, thread2_name).

In the PrintNum function, no synchronization mechanism is used to ensure that access to the global variable NUM is thread-safe. Simultaneous access to NUM by multiple threads may result in data inconsistencies or program errors. A synchronization mechanism such as a mutex should be used to ensure thread safety.

In the PrintNum function, an infinite loop is used to simulate the thread execution time. This will cause the thread to occupy CPU resources all the time and affect the performance of the system. The sleep function should be used to wait for a period of time, let the thread enter the blocked state, and release CPU resources.

In the main function, the return value of thread creation and waiting is not checked, and thread creation and waiting may fail. You should check the return value and perform error handling.

In the main function, the thread resources are not released.
The pthread_exit function or pthread_cancel function should be called after the thread execution is completed to release the thread resources.

In the main function, the return statement is not used to end the program, and the return statement should be used to end the program after the program is executed.

The above answer is the response I got when I threw the code to chatgpt.

All that said, this code needs some modification to run correctly.

Modify the code

#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;
}

Execute function output

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

It is found that in the calculation, the number of executions of the global variables by the two threads is different.

Then I did all kinds of tinkering

Until the end, did not complete my original idea

#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;
}

code output

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

In the above two sections of code, the first section does not provide any protection for the critical section. When the two threads are scheduled, the access to the critical section is also random.

The second paragraph adds condition variables and lock protection, allowing the whole process to run regularly, thus ensuring the logic of the software.

If only lock protection is added without adding condition variables, it can only guarantee that there is one thread access in a time slice of the critical section, but the order of access to the critical section cannot be guaranteed.

The use of conditional variables in C++, you can take a look at these links

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

Guess you like

Origin blog.csdn.net/weiqifa0/article/details/130256502