-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathExample_ScopedLock.cpp
More file actions
155 lines (122 loc) · 4.1 KB
/
Example_ScopedLock.cpp
File metadata and controls
155 lines (122 loc) · 4.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// ===========================================================================
// Examples_RecursiveMutex.cpp // std::recursive_mutex
// ===========================================================================
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <print>
#include <random>
#include <string>
#include <string_view>
#include <thread>
#include "../Logger/Logger.h"
// https://medium.com/@simontoth/daily-bit-e-of-c-std-scoped-lock-9cab4142f9d4
namespace Scoped_Lock_Example
{
class Player
{
private:
std::string_view m_name;
std::mutex m_mutex;
std::default_random_engine m_random_engine;
std::uniform_int_distribution<std::size_t> m_dist;
std::size_t m_score;
public:
// c'tor
Player(std::string_view name, unsigned int seed)
: m_name{ std::move(name) }, m_random_engine{ seed }, m_dist{ 1, 6 }, m_score{}
{}
// getter
std::string_view name() const { return m_name; }
std::size_t getScore() const { return m_score; }
// public interface
void incrementScore(std::size_t points) {
m_score += points;
Logger::log(std::cout, name(), " got ", points, " points => Score: ", m_score);
}
void playWith(Player& other) {
if (&other == this) {
return;
}
// retrieve our lock and the lock of the opponent
std::scoped_lock<std::mutex, std::mutex> lock{ m_mutex, other.m_mutex };
Logger::log(std::cout, name(), " plays against ", other.name());
// roll dice until one player wins, then increase the score of the winner
std::size_t points{};
std::size_t otherPoints{};
while (points == otherPoints)
{
points = roll();
otherPoints = other.roll();
}
if (points > otherPoints) {
incrementScore(points);
}
else {
other.incrementScore(otherPoints);
}
// locks are released automatically
}
private:
// roll a six-sided dice
std::size_t roll() {
return m_dist(m_random_engine);
}
};
}
void example_scoped_lock()
{
using namespace Scoped_Lock_Example;
std::random_device device{};
std::vector<std::unique_ptr<Player>> players{};
std::initializer_list<std::string_view> names =
{
"Player1", "Player2", "Player3", "Player4", "Player5",
"Player6", "Player7", "Player8", "Player9"
};
// generate players from the names using 'std::transform' algorithm
std::transform(
names.begin(),
names.end(),
std::back_inserter(players),
[&] (std::string_view name) {
return std::make_unique<Player>(name, device());
}
);
std::println("Run the game: ");
// each player plays against all other players in parallel
std::vector<std::jthread> rounds;
for (const auto& player : players) {
auto round = [&] () {
for (const auto& opponent : players) {
player->playWith(*opponent);
}
};
rounds.push_back(std::jthread { std::move(round) });
}
// join all the threads
std::for_each(
rounds.begin(),
rounds.end(),
[](auto& t) { t.join(); }
);
std::println("Done.");
// sort
std::sort(
players.begin(),
players.end(),
[] (const auto& elem1, const auto& elem2) {
return elem1->getScore() > elem2->getScore();
}
);
std::println("Final score:");
for (const auto& player : players) {
std::println("{}: {} points.", player->name(), player->getScore());
}
}
// ===========================================================================
// End-of-File
// ===========================================================================