C++ 多线程 - 条件变量以错误的方式提供数据
C++ multithreading - condition variable supplying data in a wrong way
我有这个多线程项目,我应该在其中创建酒店的模拟。
我有这个接待员结构,他们的工作是不断地寻找是否有空闲房间(在 check_free_rooms
方法中完成,accommodate_guests
是他的线程方法)。如果他找到 number_to_check_in
,is_a_room_ready
将设置为 true
,并且通知等待房间的访客线程之一。
每位客人都有一个字段,该字段是对接待员的引用(酒店只有一个),客人正在等待接待员的条件变量receptionist.cv
以[=的条件通知客人20=] 成为现实。然后,如果我没理解错的话,随机的一位客人应该得到一个房间,其他客人应该耐心等待接待员的另一个通知。
#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;
}
然而,客人拿到房间后终端出现的线路与我想象的不太一样。客人 ID 的范围从 0 到 14,房间 ID 的范围从 0 到 9,但在输出中只有房间似乎或多或少没问题。我不知道为什么来宾 ID 是一个随机的大整数,而不是我在对象构造时分配的那些。
我在多线程和条件变量方面不是很有经验,并且几乎不知道如何解决这个问题,因为我想了很多却一无所获。如果有任何帮助,我将不胜感激。
一些示例输出:
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
或
Guest 4 accommodated in room 32746
Guest -1510756368 accommodated in room 8
Guest -1510756368 accommodated in room 1
Guest -1510756368 accommodated in room 7
Guest -1510756368 accommodated in room 4
Guest -1510756368 accommodated in room 2
Guest -1510756368 accommodated in room 5
Guest -1510756368 accommodated in room 9
Guest -1510756368 accommodated in room 6
Guest -1510756368 accommodated in room 3
^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
[copy] vs Guest &guest
[reference].
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, receptionist, coffee_machine, swimming_pool);
}
我有这个多线程项目,我应该在其中创建酒店的模拟。
我有这个接待员结构,他们的工作是不断地寻找是否有空闲房间(在 check_free_rooms
方法中完成,accommodate_guests
是他的线程方法)。如果他找到 number_to_check_in
,is_a_room_ready
将设置为 true
,并且通知等待房间的访客线程之一。
每位客人都有一个字段,该字段是对接待员的引用(酒店只有一个),客人正在等待接待员的条件变量receptionist.cv
以[=的条件通知客人20=] 成为现实。然后,如果我没理解错的话,随机的一位客人应该得到一个房间,其他客人应该耐心等待接待员的另一个通知。
#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;
}
然而,客人拿到房间后终端出现的线路与我想象的不太一样。客人 ID 的范围从 0 到 14,房间 ID 的范围从 0 到 9,但在输出中只有房间似乎或多或少没问题。我不知道为什么来宾 ID 是一个随机的大整数,而不是我在对象构造时分配的那些。
我在多线程和条件变量方面不是很有经验,并且几乎不知道如何解决这个问题,因为我想了很多却一无所获。如果有任何帮助,我将不胜感激。
一些示例输出:
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
或
Guest 4 accommodated in room 32746
Guest -1510756368 accommodated in room 8
Guest -1510756368 accommodated in room 1
Guest -1510756368 accommodated in room 7
Guest -1510756368 accommodated in room 4
Guest -1510756368 accommodated in room 2
Guest -1510756368 accommodated in room 5
Guest -1510756368 accommodated in room 9
Guest -1510756368 accommodated in room 6
Guest -1510756368 accommodated in room 3
^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
[copy] vs Guest &guest
[reference].
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, receptionist, coffee_machine, swimming_pool);
}