【问题标题】:why my program throws 14000000 instead of 10000000 using threads?为什么我的程序使用线程抛出 14000000 而不是 10000000?
【发布时间】:2022-01-10 17:16:46
【问题描述】:

我编写了一个简单的 c 程序,让每个线程将其索引乘以 1000000 并将其添加到 sum 中,我创建了 5 个线程,因此逻辑答案将是 (0+1+2+3+4)*1000000 即 10000000但它会抛出 14000000 。任何人都可以帮助我理解这一点吗?

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

typedef struct argument {
    int index;
    int sum;
} arg;

void *fonction(void *arg0) {
    ((arg *) arg0) -> sum += ((arg *) arg0) -> index * 1000000;
}
int main() {
    pthread_t thread[5];
    int order[5];
    arg a;
    for (int i = 0; i < 5; i++)
        order[i] = i;
    a.sum = 0;
    for (int i = 0; i < 5; i++) {
        a.index = order[i];
        pthread_create(&thread[i], NULL, fonction, &a);
    }
    for (int i = 0; i < 5; i++)
        pthread_join(thread[i], NULL);

    printf("%d\n", a.sum);

    return 0;
}

【问题讨论】:

  • 修改了来自每个线程的共享对象(内存)本质上是不是线程安全的。
  • 所以如果对象 index 在线程之间共享意味着 4 ,3,2,1 和 0 都可以。所以说结果将乘以 5 =50000000 不是正确的吗?请如果你能给我写一个更清晰的答案,解释这个程序是如何产生 14000000 的,或者它是随机的。对不起,我很笨。

标签: c pthreads


【解决方案1】:

它是 140.. 因为行为未定义。不同机器和其他环境因素的结果会有所不同。未定义的行为是由于所有线程访问同一对象(请参阅为每个线程提供的&amp;a)导致的,该对象在创建第一个线程后被修改。

  • 当每个线程运行时,它访问相同的index(作为访问同一对象的成员(&amp;a)的一部分)。因此,线程将看到 [0,1,2,3,4] 的假设是不正确的:多个线程可能看到 index 的相同值(例如 [0,2,4, 4,4]1) 运行时。这取决于循环创建线程的调度,因为它也会修改共享对象。

  • 当每个线程更新sum 时,它必须读取和写入相同的共享内存。这本质上容易出现竞争条件和不可靠的结果。例如,它可能是缺乏内存可见性(线程 X 没有看到从线程 Y 更新的值)或者它可能是读取和写入之间的冲突线程调度(线程 X 读取,线程 Y 读取,线程 X 写入,线程Y写)等。

如果为每个线程创建 一个新的 arg 对象,那么这两个问题都可以避免。虽然总和问题可以通过适当的锁定来解决,但索引问题只能通过不共享作为线程输入给出的对象来解决。

// create 5 arg objects, one for each thread
arg a[5];

for (..) {
    a[i].index = i;
    // give DIFFERENT object to each thread
    pthread_create(.., &a[i]);
}

// after all threads complete
int sum = 0;
for (..) {
    sum += a[i].result;
}

1 即使假设当前执行 wrt 中没有竞争条件。 sum的用法,不同线程看到index值的顺序为[0,2,4,4,4],总和为14,可能如下所示:

  1. a.index
  2. 线程 A 读取 a.index (0)
  3. a.index
  4. a.index
  5. 线程 B 读取 a.index (2)
  6. a.index
  7. a.index
  8. 线程 D 读取 a.index (4)
  9. 线程 C 读取 a.index (4)
  10. 线程 E 读取 a.index (4)

【讨论】:

  • 非常感谢!这有助于我理解并且现在有意义,所以我现在要做的就是将不同的地址(不同的对象)传递给线程,以避免修改每个对象所在的线程之间的共享内存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 2021-09-10
  • 1970-01-01
相关资源
最近更新 更多