C ++多线程-条件变量以错误的方式提供数据

我有一个多线程项目,应该在其中创建一个酒店的模拟。

我有这个接待员结构,他们的工作是不断寻找是否有空闲空间(用check_free_rooms方法完成,accommodate_guests是他的线程方法)。如果他发现number_to_check_in,则将is_a_room_ready设置为true,并通知等待房间的来宾线程之一。

每个来宾都有一个引用接待员的字段(酒店中只有一个),并且来宾正在等待接待员的条件变量receptionist.cv来通知{{1} }成为真实。然后,如果我的理解正确,一个随机的客人应该得到一个房间,其他的应该耐心地等待接待员的另一通知。

receptionist.is_a_room_ready

但是,客人进入房间后,终端中出现的行与我想象的不太一样。访客ID的范围是0到14,房间ID的范围是0到9,但是在输出中似乎只有房间或多或少可以。我不知道为什么来宾ID是一个随机大整数,而不是我在对象构造时分配的ID。

我对多线程和条件变量的经验不是很丰富,并且几乎不知道如何解决该问题,因为我经过了很多思考,却一无所获。非常感谢您的帮助。

一些示例输出:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <chrono>
#include <algorithm>
#include <experimental/random>
#include <atomic>
#include <ncurses.h>
#include <condition_variable>
#include <memory>

std::mutex mx_writing;

struct Guest;

struct Room
{
    Room() {}
    int id;
    int guest_id;
    std::atomic<bool> is_ready_for_guest{true};

    void guest_arrives(int guest_id)
    {
        this->guest_id = guest_id;
        this->is_ready_for_guest = false;
    }
    void guest_leaves(int guest_id)
    {
        this->guest_id = -1;
    }
};

struct Receptionist
{
    Receptionist(std::vector<Room> &rooms) : rooms(rooms) {}
    std::vector<Room> &rooms;

    std::mutex mx;
    std::condition_variable cv;
    std::atomic<bool> is_a_room_ready{false};
    int number_to_check_in = 0;

    void check_free_rooms()
    {
        std::unique_lock<std::mutex> lock_receptionist(mx);
        do
        {
            this->number_to_check_in = std::experimental::randint(0,(int)rooms.size() - 1); //find an empty room
        } while (!rooms[this->number_to_check_in].is_ready_for_guest);
        is_a_room_ready = true;
        cv.notify_one();
    }

    void accommodate_guests()
    {
        while (true)
        {
            check_free_rooms();
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }
};

struct Guest
{
    Guest(int id,Receptionist &receptionist,Coffee_machine &coffee_machine,Swimming_pool &swimming_pool) : id(id),receptionist(receptionist),coffee_machine(coffee_machine),swimming_pool(swimming_pool) {}
    int id;
    int room_id;

    Receptionist &receptionist;
    Coffee_machine &coffee_machine;
    Swimming_pool &swimming_pool;

    void check_in()
    {
        std::unique_lock<std::mutex> lock_receptionist(receptionist.mx);
        while (!receptionist.is_a_room_ready)
        {
            receptionist.cv.wait(lock_receptionist);
        }
        receptionist.is_a_room_ready = false;
        this->room_id = receptionist.number_to_check_in;           //assign room to guest
        receptionist.rooms[this->room_id].guest_arrives(this->id); //assign guest to room && room becomes occupied
        {
            std::lock_guard<std::mutex> writing_lock(mx_writing);
            std::cout << "Guest " << this->id << " accomodated in room " << this->room_id << std::endl;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }

    void have_holiday()
    {
        check_in();
        std::this_thread::sleep_for(std::chrono::milliseconds(std::experimental::randint(500,700)));
    }
};

int main()
{
    std::vector<Room> rooms(10);
    for (int i = 0; i < 10; i++)
    {
        rooms[i].id = i;
    }

    Receptionist receptionist(rooms);

    std::vector<Guest> guests;
    for (int i = 0; i < 15; i++)
    {
        guests.emplace_back(Guest(i,receptionist,coffee_machine,swimming_pool));
    }

    std::vector<std::thread> threadList;

    threadList.emplace_back(std::thread(&Receptionist::accommodate_guests,std::ref(receptionist)));
    for (Guest guest : guests)
    {
        threadList.emplace_back(std::thread(&Guest::have_holiday,std::ref(guest)));
    }

    for (std::thread &t : threadList)
    {
        t.join();
    }

    return 0;
}

Guest 5 accommodated in room 32657
Guest -624431120 accommodated in room 7
Guest -624431120 accommodated in room 9
Guest -624431120 accommodated in room 8
Guest -624431120 accommodated in room 5
Guest -624431120 accommodated in room 4
Guest -624431120 accommodated in room 3
Guest -624431120 accommodated in room 2
Guest -624431120 accommodated in room 0
Guest -624431120 accommodated in room 6
^C

Guest 4 accommodated in room 32539
Guest -497561616 accommodated in room 1
Guest -497561616 accommodated in room 9
Guest -497561616 accommodated in room 5
Guest -497561616 accommodated in room 8
Guest -497561616 accommodated in room 6
Guest -497561616 accommodated in room 7
Guest -497561616 accommodated in room 3
Guest -497561616 accommodated in room 4
Guest -497561616 accommodated in room 0
^C
iCMS 回答:C ++多线程-条件变量以错误的方式提供数据

替换:

for (Guest guest : guests) {
    threadList.emplace_back(std::thread(&Guest::have_holiday,std::ref(guest)));
}

使用:

for (Guest &guest : guests) {
    threadList.emplace_back(std::thread(&Guest::have_holiday,std::ref(guest)));
}

在您的代码中,您将在每次迭代中创建一个来宾副本,而不是使用现有的来宾副本。
注意Guest guest [副本]与Guest &guest [参考]。
Read about references in C++

另一种解决方法:

for (int i = 0; i < 15; i++) {
    guests.emplace_back(Guest(i,receptionist,coffee_machine,swimming_pool));
}

在本节中,您将在每次迭代中两次创建来宾对象。在emplace_back中,您可以传递构造函数的参数而无需创建副本:

for (int i = 0; i < 15; i++) {
    guests.emplace_back(i,swimming_pool);
}
本文链接:https://www.f2er.com/2287668.html

大家都在问