不打印格式化字符串会影响我的多线程程序中的变量值

Not printing a formatted string affects the value of a variable in my multithreaded program

删除 printf 语句会影响程序创建子线程的循环中“i”的值。我试过打印一个未格式化的字符串,但它破坏了程序。将 printf 移动到 pthread_create() 上方会导致相同的结果。但是,在创建线程后打印任何格式化字符串将“修复”程序。有谁知道我做错了什么?

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

#define FALSE 0
#define TRUE !FALSE

pthread_mutex_t mutex;
pthread_cond_t cond1;
pthread_cond_t cond2;

int flag1 = FALSE;
int flag2 = FALSE;

void* someFunction(void* arg);
void anotherFunction(int threadNumber);

int main(int argc, char* argv[]) 
{
    pthread_t threads[6];

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond1, NULL);
    pthread_cond_init(&cond2, NULL);

    for (int i = 0; i < 6; i++)
    {
        // moving the printf to here will cause the program to hang
        pthread_create(&threads[i], NULL, &someFunction, &i);
        // not printing a formatted string here will cause the program to hang
        printf("Thread %d created\n", i);
    }
    
    pthread_mutex_lock(&mutex);

    for (int i = 0; i < 6; i++)
    {
        flag1 = TRUE;
        // go to child thread
        pthread_cond_signal(&cond1);

        // wait for child thread to change flag2
        while (flag2 == FALSE)
        {
            pthread_cond_wait(&cond2, &mutex);
        }
        
        flag2 = FALSE;
        pthread_mutex_unlock(&mutex);
    }

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond1);
    pthread_cond_destroy(&cond2);

    return 0;
}

void* someFunction(void* arg)
{
    int threadNumber = *(int*)arg;

    pthread_mutex_lock(&mutex);

    // wait for parent thread to change flag1
    while (flag1 == FALSE)
    {
        pthread_cond_wait(&cond1, &mutex);
    }

    anotherFunction(threadNumber);
    flag2 = TRUE;

    // go back to parent thread
    pthread_cond_signal(&cond2);
    pthread_mutex_unlock(&mutex);

    return NULL;
}

void anotherFunction(int threadNumber)
{
    if (threadNumber == 0)
    {
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 1)
    {        
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 2)
    {       
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 3)
    {        
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 4)
    {       
        printf("I'm thread %d!\n", threadNumber);
    }
    else if (threadNumber == 5)
    {        
        printf("I'm thread %d!\n", threadNumber);
    }
}

这是打印格式化字符串时的输出。

Thread 0 created
Thread 1 created
Thread 2 created
Thread 3 created
Thread 4 created
Thread 5 created
I'm thread 0!
I'm thread 1!
I'm thread 2!
I'm thread 3!
I'm thread 4!
I'm thread 5!

这是删除 printf 时的输出

I'm thread 1!
I'm thread 2!
I'm thread 3!
I'm thread 4!
I'm thread 5!

程序在打印“我是线程 5!”后挂起。

编辑:感谢你们的回复!这是解决方案

    for (int i = 0; i < 6; i++)
    {
        int* copy = malloc(sizeof(int));
        *copy = i;
        // moving the printf to here will cause the program to hang
        pthread_create(&threads[i], NULL, &someFunction, copy);
        // not printing a formatted string here will cause the program to hang
        printf("Thread %d created\n", i);
    }

此处将局部变量i 的地址传递给线程函数。这个变量至少在 for 循环结束时(或者甚至可能在每次迭代结束时)超出范围。这意味着循环结束后(或循环迭代结束后)线程对变量的每次访问都是未定义的行为。

    for (int i = 0; i < 6; i++)
    {
        // moving the printf to here will cause the program to hang
        pthread_create(&threads[i], NULL, &someFunction, &i);
        // not printing a formatted string here will cause the program to hang
        // printf("Thread %d created\n", i);
    }

当你使用选项-fsanitize=address -fsanitize=undefined.

用GCC编译程序时,你的程序会报这个错误

即使你固定了变量的范围,例如通过将它移动到函数 main 的范围内,您将同一变量的地址传递给所有线程,因此未定义每个线程可能看到的值 (0..6)。如果线程创建比实际启动线程快,所有线程可能会看到相同的值 6。

(如评论中所述,由于数据竞争,您甚至可能会看到其他值。如果在您的平台上对变量的访问是原子的,或者如果循环仅修改最低有效字节,如本例所示,您通常会得到一个值 0..6.)

谢谢大家的回复!解决方案是为 i

的副本动态分配一些内存
for (int i = 0; i < 6; i++)
{
    int* copy = malloc(sizeof(int));
    *copy = i;
    // moving the printf to here will cause the program to hang
    pthread_create(&threads[i], NULL, &someFunction, copy);
    // not printing a formatted string here will cause the program to hang
    printf("Thread %d created\n", i);
}