【问题标题】:Output for a symmetric multiprocessing system对称多处理系统的输出
【发布时间】:2014-11-22 03:47:33
【问题描述】:

考虑这段代码:

int x; //declared globally

for( int i=0; i<1000000; i++)
           x++;

printf("%d",x);

这是程序的核心逻辑,忽略创建线程等语法的东西。

现在,我创建了两个线程,并且程序在运行时表现出竞争条件(显然!)。

为简单起见,让我们假设整个 for 循环在一个线程中一次性运行,然后才运行第二个线程(假设是单处理器系统)。因此,理想的预期输出是第一个线程为 100 万,第二个问题为 200 万。 (虽然哪个线程打印 200 万取决于 CPU 调度程序)

我的第一个问题: 根据竞态条件的定义,我每次运行程序时,第一输出和第二输出都打乱了,是否可以认为是竞态条件?为什么?

第二个问题:考虑一个多处理系统,它在两个线程中同时运行 for 循环 100 万次。理想情况下的预期输出是多少?为什么?

【问题讨论】:

  • 首先,“跳跃”输出不能归因于与该循环有关的任何事情。其次,正是由于您已经宣布的已知竞争条件,我希望 nothing 具有任何可预测性,即使输出没有“混乱”。所以我不确定这个问题的意义是什么。你已经确定这是一个有种族条件的节日,我们只能假设你知道那是什么,那么问题到底是什么?
  • 即使有适当的程序也很难证明比赛条件。我看到大部分时间都在打印一些随机输出(这可以被认为是竞争条件)。但其他时候(很少),程序会打印x=1000000x=2000000(但不是每次都以相同的顺序)。所以我的问题是,当程序打印这些理想值时,它是否展示了竞争条件?模棱两可的是,虽然值是正确的,但顺序是错误的
  • 由于竞态条件,很难预测会发生什么,无法提前确定。最好是您创建、测试代码,然后提出一个问题,为什么会发生这种情况,而不是为您预测输出并解释。
  • 我不是要实际输出。理想情况下会是什么?考虑到每个微指令在两个处理器中彼此同步。现在输出会是什么?
  • 是的。输出应该首先是x=1000000,然后是x=2000000,对吗?有时会很混乱

标签: c multithreading multiprocessing race-condition


【解决方案1】:

我将提供一些示例代码来演示所描述的场景,但由于循环结束和 printf 打印之间的时间不同,我将消除差异。我将为此使用一个临时局部变量。

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

pthread_mutex_t x_mutex = PTHREAD_MUTEX_INITIALIZER;
int x = 0;

void *
task_function(void *ptr)
{
    int index;
    int last = 0;

    for (index = 0; index < 1000000; index++) {
        pthread_mutex_lock(&x_mutex);
        last = ++x;
        pthread_mutex_unlock(&x_mutex);
    }

    /* Use last since it won't matter how much time elapses
     * after the loop ends and when the printf is done */
    printf("x = %d\n", last);

}

int
main()
{
    pthread_t thread1, thread2;
    int retval1, retval2;

    /* Create two independent threads which will each execute task_function */
    retval1 = pthread_create(&thread1, NULL, task_function, NULL);
    retval2 = pthread_create(&thread2, NULL, task_function, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
}

如果您运行此代码,最后一个要完成的线程应始终打印 2,000,000 。如果没有,则存在严重的错误。然而,第一个完成的将打印一个真正不确定的值,除非您确切知道调度程序如何划分任务以及每个线程的运行速度。

把重点带回家。假设循环是5 而不是 1000000。假设线程以相同的速度同时运行,并且每个增量都是交错的。所以线程 0 首先递增,然后将 x 递增到 1。线程 2 递增,现在是 2。最终,如果此模式重复,线程 1 将首先执行 5 次循环,它会看到值9。然后线程 2 将完成它并显示10 的值。正如您所看到的,如果工作平均分配,这两个值理论上可以接近,具体取决于事情的安排方式。所以在这个例子中,不是打印510,而是打印910

如果没有与x 相关的正确互斥体/关键部分/原子操作,代码甚至可能不会显示 2000000。这是因为两个线程对 x 的更新可能会相互破坏,从而产生 x 的值,而这些值不是正确的。如果有人取出 task_function 中的互斥锁,您可能会发现结果非常不稳定。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-21
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多