C 执行的线程比创建的线程多

C 执行的线程比创建的线程多,c,linux,multithreading,pthreads,C,Linux,Multithreading,Pthreads,我在学习线程编程。在执行下面的示例时,我得到了未定义的行为 static void* print_thread_attr (void* param) { pthread_attr_t attr; int detach_state = PTHREAD_CREATE_JOINABLE; pthread_getattr_np (pthread_self(), &attr); pthread_attr_getdetachstate (&attr, &am

我在学习线程编程。在执行下面的示例时,我得到了未定义的行为

static void* print_thread_attr (void* param)
{
    pthread_attr_t attr;
    int detach_state = PTHREAD_CREATE_JOINABLE;

    pthread_getattr_np (pthread_self(), &attr);
    pthread_attr_getdetachstate (&attr, &detach_state);
    pthread_attr_destroy (&attr);

    printf("Detach state of thread [%u] pid [%d] is %s\n", pthread_self(), getpid(),
            (PTHREAD_CREATE_JOINABLE == detach_state)?
                    "JOINABLE THREAD":"DETACHED THREAD");
    return NULL;
}

void test_thread_attributes ()
{
    pthread_t thread_id1;
    pthread_t thread_id2;
    pthread_attr_t attr;

    printf("main pid [%d]\n", getpid());
    pthread_attr_init (&attr);
    pthread_create (&thread_id1, &attr, print_thread_attr, NULL);

    pthread_attr_init (&attr);
    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
    pthread_create (&thread_id2, &attr, print_thread_attr, NULL);

    pthread_attr_destroy (&attr);
    pthread_join (thread_id1, NULL);

    //sleep (1);
}
1.有时,它会打印:

main pid [3394]
Detach state of thread [88292160] pid [3394] is DETACHED THREAD
Detach state of thread [75705152] pid [3394] is JOINABLE THREAD
  • 但有时

     main pid [3403]
     Detach state of thread [75705152] pid [3403] is JOINABLE THREAD
     Detach state of thread [88292160] pid [3403] is DETACHED THREAD
     Detach state of thread [88292160] pid [3403] is DETACHED THREAD
    
  • 这很奇怪,因为我只使用pthread_创建了2次,但第二个案例打印了3次。使用valgrind进行检查时,第一种情况下没有内存泄漏,但第二种情况下“可能丢失:1个块中有136个字节”

    我需要知道为什么在第二种情况下,螺纹88292160打印两次?
    此外,您可能会注意到注释掉了sleep(1)。当我睡眠(1)时,我没有观察到第二种情况发生。

    您有未定义的行为,因为您在已初始化的对象上调用了两次和第二次:

    对具有以下属性的线程属性对象调用pthread_attr_init() 已初始化导致未定义的行为

    static void* print_thread_attr (void* param)
    {
        pthread_attr_t attr;
        int detach_state = PTHREAD_CREATE_JOINABLE;
    
        pthread_getattr_np (pthread_self(), &attr);
        pthread_attr_getdetachstate (&attr, &detach_state);
        pthread_attr_destroy (&attr);
    
        printf("Detach state of thread [%u] pid [%d] is %s\n", pthread_self(), getpid(),
                (PTHREAD_CREATE_JOINABLE == detach_state)?
                        "JOINABLE THREAD":"DETACHED THREAD");
        return NULL;
    }
    
    void test_thread_attributes ()
    {
        pthread_t thread_id1;
        pthread_t thread_id2;
        pthread_attr_t attr;
    
        printf("main pid [%d]\n", getpid());
        pthread_attr_init (&attr);
        pthread_create (&thread_id1, &attr, print_thread_attr, NULL);
    
        pthread_attr_init (&attr);
        pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
        pthread_create (&thread_id2, &attr, print_thread_attr, NULL);
    
        pthread_attr_destroy (&attr);
        pthread_join (thread_id1, NULL);
    
        //sleep (1);
    }
    
    如果对象
    attr
    已销毁,则可以重新使用该对象:

    一旦线程属性对象被销毁,它就可以被删除 使用pthread_attr_init()重新初始化。任何其他使用已销毁文件的行为 线程属性对象具有未定义的结果

    但是,在您的代码中,不能保证在第二个线程重新初始化属性之前,第一个线程将销毁该属性。因此,您可以使用两个不同的属性对象,甚至可以重复使用同一个属性(无需重新初始化)。但是,由于您在线程中销毁了属性,因此在代码中不可能使用此选项

    但是,最新版本删除了用于重新初始化已初始化但已销毁的属性和状态的引用未定义行为:

    如果实现检测到attr指定的值 pthread_attr_init()的参数引用已初始化的 线程属性对象,建议函数 失败并报告[EBUSY]错误

    我假设您的实现使用的是较旧的,并且会导致未定义的行为。在任何情况下,这都是一个问题,您应该检查pthread函数的返回值是否有错误


    出现重复输出的实际问题是由于此处解释的一个实现,该实现将解释为什么
    sleep
    似乎“修复”了问题,以及避免此问题的可能解决方法。

    请注意,在第二种情况下,最后两条消息的第二个线程ID相同,因此,实际上没有执行更多的线程,只有2个。如果
    main()
    没有提前结束,您是否也注意到了这一点?如果没有,请通过调用
    pthread\u exit()
    尝试离开
    main()
    。可能是:Hi的副本,我已通过:1检查了您的答案。不重新初始化属性;2.使用两种不同的属性,但仍要观察第二种情况。关于销毁线程中的属性,如果我不销毁它,valgrind将报告2个泄漏。在手册页中,如果在pthread\u getattr\u np之后没有使用该属性,也建议销毁该属性。因此,我认为销毁线程内的属性不会影响在main中初始化的属性(因为它位于不同的内存资源中)。请纠正我的错误。感谢您对主出口数据竞争的提示,我已经理解了为什么sleep 1解决了这个问题。当然,由属性引起的UB与dup输出无关。当不再需要某个属性时,可以将其销毁。但是在您的代码中,当一个线程初始化时(当它可能被另一个线程破坏或没有被破坏时),存在着对attrbute的竞争。我认为销毁线程内的属性不会影响在main中初始化的属性(因为它位于不同的内存资源中。不,它是同一个属性对象。请记住,所有线程共享相同的地址空间。