【问题标题】:C Semaphore strange precedence behaviorC Semaphore 奇怪的优先级行为
【发布时间】:2015-06-20 01:10:20
【问题描述】:

我在 C 中练习并发,我似乎遇到了一些信号量问题。 我在 MacOSX 中使用 Xcode 6.3.2。

这是一个看起来很奇怪的示例程序:该示例的目的是打印 ABCD 或 BACD 字符串

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

void *thread1(void*);
void *thread2(void*);

sem_t *sem0, *sem1, *sem2;;

int main(int argc, const char * argv[]) {

    pthread_t t1, t2;

    sem0 = sem_open("sem0", O_CREAT, 0600, 2);
    sem1 = sem_open("sem1", O_CREAT, 0600, 0);
    sem2 = sem_open("sem2", O_CREAT, 0600, 0);

    // quick check
    if (sem0 == SEM_FAILED || sem1 == SEM_FAILED || sem2 == SEM_FAILED) {
        printf("Something went wrong\n");
        return 0;
    }

    pthread_create(&t1, NULL, thread1, NULL);
    pthread_create(&t2, NULL, thread2, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    sem_close(sem0);
    sem_close(sem1);
    sem_close(sem2);

    return 0;
}

void *thread1(void* arg) {
    int n=0;
    while (n<10) {
        sem_wait(sem0);
        printf("A");
        fflush(stdout);
        sem_wait(sem1);
        printf("C");
        fflush(stdout);
        sem_post(sem2);
        n++;
    }
    pthread_exit(NULL);
}

void *thread2(void* arg) {
    int n=0;
    while (n<10) {
        sem_wait(sem0);
        printf("B");
        fflush(stdout);
        sem_post(sem1);
        sem_wait(sem2);
        printf("D\n");
        fflush(stdout);
        sem_post(sem0);
        sem_post(sem0);
        n++;
    }
    pthread_exit(NULL);
}

如果我正确实现了信号量,结果将是 ABCD 或 BACD,但实际上我会得到各种奇怪的输出。

我将在此处包含部分输出

ABCD
BAD
CABCD
BAD
CBAD
CBAD
CBAD
CBAD
CBAD
CBAD
C

有人可以帮助我吗?提前致谢

重要编辑: 我下载了 Ubuntu,代码在那里运行顺利,完全没有问题。所以,继续

  • MacOSX 10.10.3 和 Xcode 6.3.2 --> 不能正常工作
  • Ubuntu 15.04 --> 正常工作

不知道为什么。

【问题讨论】:

  • @CrApHer 你知道它是否返回错误和/如果返回,sem_wait 在 SEM_FAILED 信号量参数上的行为吗?
  • 你的线程函数有错误的签名。他们每个人都应该接受void * 类型的参数,即使他们不使用它。实际上,您的编译器应该警告指针类型不匹配。
  • 在 Fedora 21 上测试你的代码,我非常可靠地看到每次都打印出序列“BACD\n”,进行了数千次迭代。这是合理的——但不能保证的——行为。我在您的代码中没有看到任何解释您突出显示的意外输出的内容。
  • @Ivano 这是您第一次运行程序时发生的,还是在随后的调用中发生的?
  • @JohnBollinger 我编辑了线程签名,这并没有改变任何东西,但感谢您指出这一点以完成。关于你在 Fedora 上的测试,会不会是在 MacOSX 环境下有些奇怪,而在纯 linux 下结果会如预期?

标签: c concurrency semaphore


【解决方案1】:

您的问题可能是printf 输出被缓冲并在线程之间共享。在发布到信号量之前,您必须确保使用 fflush 清空缓冲区。

【讨论】:

  • 在我自己以前的一个程序中,使用mutexcondition,我还发现printf()很野,在我添加fflush(stdout)后,控制台中的输出变得完全正确,所以我认为这个答案是绝对正确的,以防操作正确地编写并发部分。顺便说一句,fflush() 需要时间,在我的程序中,它使线程切换的频率不同,或者线程的执行变得更加有序,所以在用printf()fflush() 证明程序是正确的之后,只需取消注释两者其中。
  • 我不确定我是否正确实施了您的更改,您可以在 op 中看到修改后的代码,但我不断获得奇怪的输出。我真的不明白为什么,这可能与我在 MacOsx 上而不是在纯 Linux 上有关吗?
【解决方案2】:

当您为sem2 调用sem_open() 时出现拼写错误 - 传入的名称是"sem1"

sem1 = sem_open("sem1", O_CREAT, 0600, 0);
sem2 = sem_open("sem1", O_CREAT, 0600, 0);
//               ^^^^

所以指针sem1sem2 将指向同一个信号量。

【讨论】:

  • 感谢您的注意,我编辑了代码,但奇怪的行为仍然令人难以置信。 Ubuntu 快下载完了,我会尝试在那个环境中运行作为最后的希望。
  • 我看到您还更新了示例输出 - 所以行为至少改变了?您可以尝试将对printf("A");printf("B"); 的调用包装在一对flockfile(stdout)/funlockfile(stdout) 调用中(并且可能删除fflush() 调用-我认为它们没有必要)?根据我对 POSIX 的理解,printf() 应该已经在做同样的事情,所以我不希望有什么不同,但我认为值得一试。
【解决方案3】:

Printf 正在访问一种资源,即标准输出文件描述符。您需要请求锁定以允许一次仅 一个线程 访问标准输出。否则你可能会得到交错的文本。我不确定你为什么要创建三个信号量,但考虑一下,如果你有多个信号量阻塞同一个资源,你就会有多个窗口对该资源打开。如果你一次只关闭一个,那么你根本就没有真正阻塞资源。

编辑: 一个例子。线程 1 锁定 sem 0,线程 2 等待。线程 1 解锁 sem0,线程 2 可以继续。线程 1 现在锁定 sem1。 虽然他们都在写标准输出。这意味着他们俩,即使遵守你的锁,也被允许写入标准输出。

为什么它没有更早发生只是由于线程争用。

编辑 2: 我认为约翰是对的,因为我最初的解释没有涵盖您的问题。我无法重现您的输出,也无法想象您的输出路径。即使使用其他人提到的 fflush 示例,即使使用与信号量应该工作的方式相同的缓冲区,我仍然看不到该特定字符串将如何形成。我只能猜测:

  1. 您的信号量已命名,您不会破坏它们。这意味着它们在您的程序调用之间持续存在。如果您在正确的时间结束程序,信号量值可能会处于您不期望的状态(如果信号量存在,它们会忽略 sem_open 的 value 参数)。 也许这可能会导致您的输出。
  2. 您没有检查 sem 函数的返回值,它们可能会返回错误。对无效信号量调用后续函数应立即返回,从而使您的 printfs 仅受线程争用的影响。

【讨论】:

  • 代码、多个信号量等的要点是允许以任一顺序打印“A”和“B”(即完全按照您所说的可能发生交错),始终遵循按固定顺序按“C”和“D”。在这一点上,为什么提供的代码不能实现这一点对我来说并不明显。您的回答在一般意义上是可以的,但它没有充分解决问题中提出的具体情况。
  • 感谢您指出我没有关闭信号量,现在我这样做了,它解决了我遇到的另一个奇怪行为(由于之前未正确关闭信号量而忽略了信号量的起始值)我'我现在也在检查信号量的返回值,正如您在 op 中编辑的代码中看到的那样,但奇怪的问题仍然存在。
猜你喜欢
  • 1970-01-01
  • 2013-03-16
  • 1970-01-01
  • 1970-01-01
  • 2019-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-16
相关资源
最近更新 更多