【问题标题】:More threads executed than created执行的线程多于创建的线程
【发布时间】:2015-05-07 09:19:46
【问题描述】:

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

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
  1. 但有时

     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
    

这很奇怪,因为我只使用了 2 次 pthread_create,但第 2 种情况打印了 3 次。使用 valgrind 检查时,第一种情况没有内存泄漏,但第二种情况,“可能丢失:1 个块中的 136 个字节”

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

【问题讨论】:

  • 请注意,在第二种情况下,最后两条消息的第二个线程 ID 是相同的,所以不,实际上没有更多线程被执行,只有 2 个。
  • 如果main() 没有提前结束,你是否也观察到这一点?如果没有,请尝试通过调用 pthread_exit() 离开 main()。
  • 可能与以下内容重复:stackoverflow.com/q/26211423/694576

标签: c linux multithreading pthreads


【解决方案1】:

你有未定义的行为,因为你在一个已经初始化的对象上调用了两次和第二次 pthread_attr_init():

在线程属性对象上调用 pthread_attr_init() 已经初始化会导致未定义的行为。

对象attr被销毁后可以重用:

一旦线程属性对象被销毁,它可以 使用 pthread_attr_init() 重新初始化。销毁的任何其他用途 线程属性对象有未定义的结果。

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

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

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

我假设您的实现使用的有点旧,并导致未定义的行为。无论如何,这是一个问题,您应该检查 pthread 函数的返回值是否有错误。

看到的重复输出的实际问题是由于issue with the stdio 中的实现an answer 这里解释了为什么sleep 似乎“修复”了该问题以及避免此问题的可能解决方法。

【讨论】:

  • 您好,我已经通过以下方式检查了您的答案: 1. 不重新初始化属性; 2. 使用 2 个不同的属性,但仍然观察第 2 种情况。关于销毁线程中的属性,如果我不销毁它,valgrind会报告2个泄漏。并且从手册页中,如果在 pthread_getattr_np 之后没有使用它,还建议销毁该属性。因此,我认为破坏线程内的属性不会影响在 main 中初始化的属性(因为它位于不同的内存资源中)。请纠正我。感谢您对主出口数据竞争的提示,我明白为什么 sleep 1 解决了这个问题
  • 当然属性引起的UB与dup输出无关。当您不再需要某个属性时,您可以销毁它。但是在您的代码中,当一个线程初始化时(当它可能被其他线程破坏时),属性存在一个 race。 我认为在线程内销毁属性不会影响在 main 中初始化的属性(因为它在不同的内存资源中。不,它是同一个属性对象。记住所有线程共享相同的地址空间。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-26
相关资源
最近更新 更多