【问题标题】:C in printing function mutex does not work properlyC中的打印功能互斥量不能正常工作
【发布时间】:2020-04-20 12:36:22
【问题描述】:

我试图给线程一个 id,然后我想打印我给的每个线程 id,但是我猜有一个关于互斥锁的情况,我想我正在处理关键部分,但似乎我不能.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

struct info{
    int id;
    int flag;
};

void *print_info(void* arg){
    struct info *arg_struct = (struct info*) arg;

    pthread_mutex_lock(&m);

    printf("%d ", arg_struct->id);
    pthread_mutex_unlock(&m);

    pthread_exit(0);

}
int main(int argc, char *argv[])
{
    int number = 10;
    pthread_t tid[number];
    for (int i = 0; i < number; ++i) {
        int info[2];
        info[0] = i;
        info[1] = 0;

        pthread_create(&tid[i], NULL, print_info, &info);
    }
    for (int i = 0; i < number; ++i) {
        pthread_join(tid[i], NULL);
    }

    return 0;
}

这是输出: 1 2 3 4 5 6 7 8 9 9

每次我执行它都会有所不同,但或多或​​少的概念是相同的,它不会打印一些值,它会多次打印一些值。

但预期的输出是这样的: 0 1 2 3 4 5 6 7 8 9

[或者一些不按顺序的东西,但我猜到每个值都会被准确地打印出来] 谢谢

【问题讨论】:

  • info 是本地块内部。当它超出范围时,此本地被破坏,因此您正在访问线程函数中的悬空指针。将此数组分配到堆上,例如使用malloc/calloc 函数。
  • 你之前说的我试过了,结果还是一样,你说一次后我试过了,没有任何变化
  • 线程函数将指向int[2] 的指针转换为指向struct info 的指针。不好。
  • 还有一场竞赛,因为新生成的线程访问了arg 指向的内容,而创建线程的循环已经用新值i 覆盖了int[0]。不好。

标签: c multithreading pthreads mutex


【解决方案1】:

它失败了,因为您不断将指向实际上是相同向量的指针传递给子线程,并在子线程运行时迅速覆盖主线程中的内容。

在加载系统的情况下,您同样可以得到所有 9 的输出。甚至是相反的数字(只是因为我不记得在任何地方都可以保证线程将按照与您创建线程相同的顺序进行调度)。或者一个非常奇怪的数字,因为如果子线程直到主线程开始连接才开始运行,那么无论 'arg' 指向什么,都将位于 pthread_join 调用堆栈的中间。

所有互斥锁保护您免受同时输出到标准输出的线程的影响。

顺便说一句,将整数数组转换为 2 个整数的结构是未定义的行为,这完全是另一回事。

【讨论】:

    【解决方案2】:

    给每个线程自己的控制数据

    正如所写,您的代码不能保证输出数字的顺序——线程执行的顺序取决于操作系统和硬件。通过确保每个线程都有自己的struct info 可以使用,您可以轻松地确保每个 ID 只打印一次。您现有的代码 (a) 对具有结构的数组进行类型双关,这是一个坏主意(alk's comment),并且 (b) 在主程序的堆栈上重用相同的空间,以便在线程在工作,主循环改变存储的值是可行的。

    这就是@rafix07comment 中所说的。您声称已尝试修复但未成功。我必须得出结论,您修改后的代码没有做必要的事情。

    您需要使用更像这样的代码(在每次运行结束时也会打印一个换行符):

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
    
    struct info
    {
        int id;
        int flag;
    };
    
    static void *print_info(void *arg)
    {
        struct info *arg_struct = (struct info *)arg;
    
        pthread_mutex_lock(&m);
    
        printf("%d ", arg_struct->id);
        pthread_mutex_unlock(&m);
    
        pthread_exit(0);
    }
    
    int main(void)
    {
        int number = 10;
        pthread_t tid[number];
        struct info info[number];
        for (int i = 0; i < number; ++i)
        {
            info[i].id = i;
            info[i].flag = 0;
            pthread_create(&tid[i], NULL, print_info, &info[i]);
        }
    
        for (int i = 0; i < number; ++i)
        {
            pthread_join(tid[i], NULL);
        }
        putchar('\n');
    
        return 0;
    }
    

    当我连续运行 10 次时,我得到了输出:

    0 7 2 8 3 1 4 9 5 6 
    1 8 0 9 2 3 6 5 4 7 
    0 4 5 2 3 6 7 1 8 9 
    0 1 2 3 4 5 6 7 8 9 
    2 7 0 3 5 6 4 8 1 9 
    2 0 7 6 3 5 4 8 9 1 
    0 9 1 3 5 6 7 2 8 4 
    0 7 1 8 4 3 9 2 5 6 
    0 7 1 8 3 5 4 2 9 6 
    0 3 4 5 6 1 2 8 7 9 
    

    如您所见,每个数字 0..9 在输出的每一行中都出现一次,但线程执行的顺序并不确定。

    我不相信互斥锁会为你买任何东西。所有 I/O 函数(例如 printf())的行为必须如同它们在进入时使用 flockfile() 而在返回时使用 funlockfile()

    在运行 macOS Mojave 10.14.6(别问!)和 GCC 9.2.0 的 MacBook Pro 上测试。

    保证顺序

    一个简单的修改确保了顺序——主线程在启动线程之前锁定互斥锁,而线程在退出之前解锁它。然而,这意味着没有有意义的并发——你不妨这样写:

    for (int i = 0; i < 10; i++)
        printf("%d ", i);
    putchar('\n');
    

    这将避免在线程之后启动、运行和清理的所有开销。

    修改后的代码只是将print_info()函数中的一行移到main()中:

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
    
    struct info
    {
        int id;
        int flag;
    };
    
    static void *print_info(void *arg)
    {
        struct info *arg_struct = (struct info *)arg;
    
        printf("%d ", arg_struct->id);
        pthread_mutex_unlock(&m);
    
        pthread_exit(0);
    }
    
    int main(void)
    {
        int number = 10;
        pthread_t tid[number];
        struct info info[number];
        for (int i = 0; i < number; ++i)
        {
            info[i].id = i;
            info[i].flag = 0;
            pthread_mutex_lock(&m);
            pthread_create(&tid[i], NULL, print_info, &info[i]);
        }
    
        for (int i = 0; i < number; ++i)
        {
            pthread_join(tid[i], NULL);
        }
        putchar('\n');
    
        return 0;
    }
    

    还有输出:

    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    0 1 2 3 4 5 6 7 8 9 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-10
      • 2017-09-13
      • 2021-09-16
      • 1970-01-01
      • 2020-04-26
      相关资源
      最近更新 更多