【问题标题】:Differences between POSIX threads on OSX and LINUX?OSX 和 LINUX 上的 POSIX 线程之间的区别?
【发布时间】:2013-08-23 06:35:38
【问题描述】:

任何人都可以解释一下,当下面的代码在 OSX 上编译和运行时,'bartender' 线程会以一种随机的方式跳过 sem_wait(),但是当在 Linux 机器上编译和运行时sem_wait() 保持线程直到对 sem_post() 进行相对调用,正如预期的那样?

我目前不仅在学习 POSIX 线程,而且在学习并发性,因此非常欢迎任何 cmets、提示和见解...

提前致谢。

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>

//using namespace std;

#define NSTUDENTS 30
#define MAX_SERVINGS 100

void* student(void* ptr);
void get_serving(int id);
void drink_and_think();

void* bartender(void* ptr);
void refill_barrel();


// This shared variable gives the number of servings currently in the barrel
int servings = 10;

// Define here your semaphores and any other shared data
sem_t *mutex_stu;
sem_t *mutex_bar;

int main() {
    static const char *semname1 = "Semaphore1";
    static const char *semname2 = "Semaphore2";

    pthread_t tid;

    mutex_stu = sem_open(semname1, O_CREAT, 0777, 0);
    if (mutex_stu == SEM_FAILED)
    {
        fprintf(stderr, "%s\n", "ERROR creating semaphore semname1");
        exit(EXIT_FAILURE);
    }
    mutex_bar = sem_open(semname2, O_CREAT, 0777, 1);
   if (mutex_bar == SEM_FAILED)
    {
        fprintf(stderr, "%s\n", "ERROR creating semaphore semname2");
        exit(EXIT_FAILURE);
    }

    pthread_create(&tid, NULL, bartender, &tid);
    for(int i=0; i < NSTUDENTS; ++i) {
        pthread_create(&tid, NULL, student, &tid);
    }

    pthread_join(tid, NULL);

    sem_unlink(semname1);
    sem_unlink(semname2);

    printf("Exiting the program...\n");
}


//Called by a student process. Do not modify this.
void drink_and_think() {
    // Sleep time in milliseconds
    int st = rand() % 10;
    sleep(st);
}

// Called by a student process. Do not modify this.
void get_serving(int id) {
    if (servings > 0) {
        servings -= 1;
    } else {
        servings = 0;
    }
    printf("ID %d got a serving. %d left\n", id, servings);
}

// Called by the bartender process.
void refill_barrel()
{
    servings = 1 + rand() % 10;
    printf("Barrel refilled up to -> %d\n", servings);
}

//-- Implement a synchronized version of the student
void* student(void* ptr) {
    int id = *(int*)ptr;
    printf("Started student %d\n", id);
    while(1) {
        sem_wait(mutex_stu);
        if(servings > 0) {
            get_serving(id);
        } else {
            sem_post(mutex_bar);
            continue;
        }
        sem_post(mutex_stu);
        drink_and_think();
    }
    return NULL;
}

//-- Implement a synchronized version of the bartender
void* bartender(void* ptr) {
    int id = *(int*)ptr;
    printf("Started bartender %d\n", id);
    //sleep(5);
    while(1) {
        sem_wait(mutex_bar);
        if(servings <= 0) {
            refill_barrel();
        } else {
            printf("Bar skipped sem_wait()!\n");
        }
        sem_post(mutex_stu);
    }
    return NULL;
}

【问题讨论】:

  • 您没有正确使用同步原语。如果您需要互斥锁,请使用 pthread_mutex_t。如果你需要一个信号量,不要称它为“互斥量”,它不是一个。
  • 这总是在启动时打印一次Bar skipped sem_wait() 消息(当您从servings=10 和mutex_bar=1 开始时),但对我来说,在Linux 和OSX 上运行都很好。在将信号量名称更改为有效后,在 FreeBSD 上也可以正常运行(FreeBSD 对此更加挑剔)。
  • 感谢您的评论。我显然有一些研究要做......没有比现在更好的时间了;)

标签: linux multithreading macos concurrency pthreads


【解决方案1】:

第一次运行程序时,您正在创建具有初始值的命名信号量,但由于您的线程永远不会退出(它们是无限循环),因此您永远不会调用 sem_unlink 来删除这些信号量。如果您终止程序(使用 ctrl-C 或任何其他方式),信号量仍将以其所处的任何状态存在。因此,如果您再次运行该程序,sem_open 调用将成功(因为您不使用O_EXCL),但它们不会重置信号量值或状态,因此它们可能处于某种奇怪的状态。

所以你应该确保在程序启动时调用sem_unlink,然后再调用sem_open。更好的是,根本不要使用命名信号量——使用sem_init 来初始化几个未命名的信号量。

【讨论】:

  • 好心的先生,你是我目前最喜欢的人,谢谢。你能帮我确认一下吗:如果我要使用 sem_init() 并继续退出程序而不在所述未命名的信号量上调用 sem_destroy() ,我还能期望遇到未定义的行为吗?
  • 不,没有必要在退出程序之前实际调用sem_destroy,因为退出会自动销毁信号量。这就是使用未命名信号量的关键——它们除了地址之外没有其他名称,如果该地址是进程私有的(不在共享内存中),那么只有该进程可以访问该信号量。当你重新启动程序时,你会得到新的内存和新的信号量,之前运行的所有数据都消失了。
  • 感谢您的回复,您帮了大忙。当涉及到 Pthreads 时,您能否帮助我理解 OSX 和 Linux 行为之间的进一步区别?如果我使用 sem_init() Linux 将上面的命名信号量替换为未命名的信号量,则再次按预期运行,并且 OSX 让我的“调酒师”线程通过 sem_wait() 调用疯狂运行,就好像 t 不存在一样。由于我对操作系统处理这些信号量的方式了解有限,这是另一个逻辑错误吗?
  • 我没有看到您在 OSX 上描述的行为——当我运行它时它运行良好(如果我先删除命名信号量以便正确重新创建它们,它运行良好),所以我怀疑您的 OSX 设置可能有些奇怪/损坏。
  • 这非常有趣,因为我确信我在 MacBook Pro 和 Mac Pro 上都体验过 ML 上的确切行为。谜团还在继续。无论如何,非常感谢您的所有帮助,干杯。
猜你喜欢
  • 2012-02-04
  • 2018-12-31
  • 2010-12-28
  • 2013-07-20
  • 1970-01-01
  • 1970-01-01
  • 2015-06-08
  • 2022-10-28
相关资源
最近更新 更多