【问题标题】:How to tell when all instances of Thread A have finished from Thread B如何判断线程 A 的所有实例何时从线程 B 完成
【发布时间】:2021-12-11 06:37:45
【问题描述】:

我目前正在用 C++ 编写一个程序,该程序使用线程模拟餐厅中的服务员和顾客。该程序运行 40 个客户线程和 3 个服务员线程,功能如下:

void *customer(void * vargp){
    //depending on assigned table, call waiter 1-3 using semaphore
    sem_post(&callWaiter);
    //wait for waiter to respond
    sem_wait(&waiterResponse);
    //leave table and finish thread
}

void *waiter(void *vargp){
    //this is what I'm trying to fix
    while(there are still customers){ //this check has been a number of different attempts
        sem_wait(&callWaiter);
        sem_post(&waiterResponse);
    }
}

我试图找到解决方案的问题是服务员会看到客户仍然活跃(通过我尝试的几种不同技术),然后阻止等待信号量,然后客户将完成并且会有不再是客户。有没有人知道服务员线程如何在最后一个客户完成之前检查正在运行的客户而不通过检查?

【问题讨论】:

  • 它看起来像一个直接使用C库pthread的C程序。如果真的是 C++,为什么不使用标准 C++ std::threads 及其支持函数和类?
  • 如果只剩下一个顾客,三个服务员同时拨打sem_wait()怎么办?通用方法存在根本缺陷。
  • 添加到 Ted 的评论中。既然您说您正在“使用”并且可能使用 C++ 编译,请使用 C++11(或更高版本)解决方案。它会简单得多。看看像 , , , . 这样的东西
  • 我很抱歉,我应该澄清一下。我在学校的 linux 机器上运行这个程序(按照说明),它使用的是 c++ 98
  • 建议:为教师提供他们想要的东西,但用现代材料补充您的教育,以简化向劳动力的过渡。

标签: c++ multithreading concurrency


【解决方案1】:

对于简单的状态,可以使用原子变量:

//  waiter
myId= waiter id
while(work)
{
    work=false;
    for(int n=0;n<40;n++)
    while (customerWaiterMatrix[n][myId].load()>0) // one of 120 atomic variables
    {
           work=true;
           std::lock_guard<std::mutex>(mutexOfThatCustomerThisWaiter);
           // thread safe customer logic
    }
}

哪里可以这样添加:

// customer 
myId = customer id
customerWaiterMatrix[myId][selectedWaiterId].fetch_add(1);
same lock of current customer & waiter with lock guard
Do safe logic
customerWaiterMatrix[myId][selectedWaiterId].fetch_add(-1);

减少了锁定争用,之后发生的事情没有歧义。

【讨论】:

  • 我总是对if (atomic_read) { lock mutex and do stuff } 之类的东西持怀疑态度。原子读取是安全的,但没有什么能阻止另一个线程潜入读取和互斥锁之间
  • 最好有一个循环数组和原子读写增量,如并发队列,但使用原子索引分配,这样服务员就不会相互影响。
  • 他说专门从一位顾客那里叫了一位服务员。所以服务员不会互相交叉。但是,如果它是免费服务,那你是对的。我的意思是,有 40x3= 120 个原子变量,每个都是独立的。
【解决方案2】:

您正在寻找的是condition variables。它们在 pthreads 中,现在是 C++11 线程库的一部分。它们是专门为解决您遇到的问题而构建的,因为您遇到的问题通常无法通过互斥锁和 seamphore 解决。

但是,您需要的特定版本可能是可以解决的。我不知道你的特别作业问题是什么,但它可能与sleeping barber problem 非常相似,它有几个已知的解决方案,其中至少一个可以在上面的维基百科链接中找到。

【讨论】:

  • 感谢您链接本文。我会进一步研究它是否有我需要的解决方案
【解决方案3】:

对于信号量,一种直接的方法是在初始化为客户数量的信号量上使用sem_getvalue()

int num_customers(bool = false){
    int v;
    sem_getvalue(&customers, &v);
    return v;
}

当客户离开时,每个人都会对该信号量执行sem_wait()。当sem_getvalue()返回0时,表示所有客户都离开了。

最后一位顾客叫醒服务员让他们知道他们已经离开了。

    //leave table and finish thread
    sem_wait(&customers);
    if (num_customers() == 0) sem_post(&callWaiter);

服务员在接待顾客之前会先检查是否有顾客。

    while(num_customers(true) > 0){

        //wait for work
        sem_wait(&callWaiter);
        if (num_customers(true) == 0) break;

        //work
        sem_post(&waiterResponse);
    }

最后一位顾客叫醒服务员的技巧是一场无害的竞赛,多个顾客可能每个人都认为自己是最后一位,因此服务员收到多个帖子,每个帖子都表明没有更多顾客。那些额外的帖子是无害的,因为服务员无论如何都会退出。但是,可以通过将另一个信号量初始化为 1 来避免这种竞争,该信号量用于让第一个检测到没有更多顾客的顾客成为唤醒服务员的人。

int num_customers(bool is_waiter = false){
    int v;
    sem_getvalue(&customers, &v);
    if (is_waiter) return v;
    if (v == 0) {
        if (sem_trywait(&last_customer) == -1) {
            assert(errno == EAGAIN);
            return 1;
        }
    }
    return v;
}

【讨论】:

  • 感谢您的回复。您使用无害竞争条件的解决方案正是我所需要的
猜你喜欢
  • 1970-01-01
  • 2014-07-19
  • 1970-01-01
  • 2014-01-06
  • 1970-01-01
  • 2013-01-14
  • 1970-01-01
  • 1970-01-01
  • 2021-07-19
相关资源
最近更新 更多