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