如何判断线程 A 的所有实例何时从线程 B 完成

How to tell when all instances of Thread A have finished from Thread B

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

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);
    }
}

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

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

//  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);

减少了锁定争用,什么之后发生什么就没有歧义。

您要找的是condition variables。它们在 pthreads 中,现在是 C++11 线程库的一部分。它们是专门为解决您遇到的问题而构建的,因为您遇到的问题通常不能用互斥体和信号量来解决。

不过,你需要的具体版本可能是可以解决的。我不知道你的作业问题是什么,但它可能非常类似于 sleeping barber problem,它有几个已知的解决方案,其中至少一个可以在上面的维基百科 link 上找到。

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

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

当客户离开时,每个人都会对该信号量执行 sem_wait()。当sem_getvalue() returns 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;
}