cześć
jako, że zapoznaję się z tematem wielowątkowści pozwolilem sobie napisać klasyczny już problem kolacji filozofów (problem deadlocków i starvation).
wydaje mi się, że to co napisane poniżej jest w OK
ale nie umiem testować deadlocków i thread starvation

bardzo bym prosił o ocenę ewentualne uwagi i wytłumaczenie jak sprawdzać czy kod jest odporny na w/w dolegliwości

próbuję sprawdzać, czy każdy z filozofów robi wszystko (wychodzi na to, że tak, wynik poniżej) i po tym wnioskuję, że nie ma deadlocków ani głodzenia (ale nie wiem czy to co robię ma sens)

P1 eating.: 876
P1 thinking.: 876
P2 eating.: 876
P2 thinking.: 876
P3 eating.: 1016
P3 thinking.: 1016
P4 eating.: 954
P4 thinking.: 954
P5 eating.: 1030
P5 thinking.: 1030
P6 eating.: 1103
P6 thinking.: 1103

z góry dziekuję za pomoc

std::mutex print_mutex;
std::map<std::string, int>  all;

struct table
{
	std::atomic<bool>                ready{ false };
	std::vector<std::mutex>			 forks;

	table(int iforks)
		:forks(std::move(std::vector<std::mutex>(iforks)))
	{
	}
};

struct philosopher
{
private:
	const std::string	name;
	table			  &dinnertable;
	int                left_fork_id;
	int 			   right_fork_id;
	std::thread			philosopher_thread;
	std::mt19937		rng{ std::random_device{}() };

public:
	philosopher(const std::string &name, table & table, int left_fork, int  right_fork)
		: name(name)
		, dinnertable(table)
		, left_fork_id(left_fork)
		, right_fork_id(right_fork)
		, philosopher_thread(&philosopher::dinner, this)
	{
	}

	void dinner()
	{
		while (!dinnertable.ready);

		do
		{
			think();
			eat();
		} while (dinnertable.ready);
	}

	void think()
	{
		static thread_local std::uniform_int_distribution<> wait(1, 10);//generates random number
		//std::this_thread::sleep_for(std::chrono::milliseconds(wait(rng) * 50));

		print(name + " thinking.");
	}

	void eat()
	{
		std::lock(dinnertable.forks[left_fork_id], dinnertable.forks[right_fork_id]);
		std::lock_guard<std::mutex> lock_a(dinnertable.forks[left_fork_id], std::adopt_lock);
		std::lock_guard<std::mutex> lock_b(dinnertable.forks[right_fork_id], std::adopt_lock);

		static thread_local std::uniform_int_distribution<> wait(1, 10);//generates random number
		//std::this_thread::sleep_for(std::chrono::milliseconds(wait(rng) * 50));

		print(name + " eating.");

	}

	void print(const std::string &text)
	{
		std::lock_guard<std::mutex> lock_print(print_mutex);
		//std::cout << text << std::endl;

		all[text]++;

	}

	~philosopher()
	{
		philosopher_thread.join();
	}
};

void phdinner()
{
	table table(6);

	std::vector<std::unique_ptr<philosopher>> philosophers;

	philosophers.emplace_back(std::make_unique<philosopher>("P1", table, 0, 1));
	philosophers.emplace_back(std::make_unique<philosopher>("P2", table, 1, 2));
	philosophers.emplace_back(std::make_unique<philosopher>("P3", table, 2, 3));
	philosophers.emplace_back(std::make_unique<philosopher>("P4", table, 3, 4));
	philosophers.emplace_back(std::make_unique<philosopher>("P5", table, 4, 5));
	philosophers.emplace_back(std::make_unique<philosopher>("P6", table, 5, 0));


	table.ready.store(true);
	std::this_thread::sleep_for(std::chrono::milliseconds(500));
	table.ready.store(false);
}

int main()
{
	
	{
		phdinner();
	}

	for (const auto &item : all)
	{
		std::cout << item.first << ": " << item.second << std::endl;
	}

	return 0;
}