Skip to content

Commit 3722c7c

Browse files
committed
TVar demo.
1 parent bee677d commit 3722c7c

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

demos/tvar-demo.rb

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
require 'concurrent'
2+
3+
class UnsynchronizedBank
4+
5+
def initialize(account_totals)
6+
@accounts = account_totals.dup
7+
end
8+
9+
def transfer(from, to, sum)
10+
if @accounts[from] < sum
11+
false
12+
else
13+
@accounts[from] -= sum
14+
@accounts[to] += sum
15+
true
16+
end
17+
end
18+
19+
def grand_total
20+
@accounts.inject(0, :+)
21+
end
22+
23+
end
24+
25+
class CoarseLockBank
26+
27+
def initialize(account_totals)
28+
@accounts = account_totals.dup
29+
@lock = Mutex.new
30+
end
31+
32+
def transfer(from, to, sum)
33+
@lock.synchronize do
34+
if @accounts[from] < sum
35+
false
36+
else
37+
@accounts[from] -= sum
38+
@accounts[to] += sum
39+
true
40+
end
41+
end
42+
end
43+
44+
def grand_total
45+
@accounts.inject(0, :+)
46+
end
47+
48+
end
49+
50+
class FineLockBank
51+
52+
Account = Struct.new(:lock, :value)
53+
54+
def initialize(account_totals)
55+
@accounts = account_totals.map do |v|
56+
Account.new(Monitor.new, v)
57+
end
58+
end
59+
60+
def transfer(from, to, sum)
61+
locks = [@accounts[from].lock, @accounts[to].lock]
62+
ordered_locks = locks.sort{ |a, b| a.object_id <=> b.object_id }
63+
64+
ordered_locks[0].synchronize do
65+
ordered_locks[1].synchronize do
66+
if @accounts[from].value < sum
67+
false
68+
else
69+
@accounts[from].value -= sum
70+
@accounts[to].value += sum
71+
true
72+
end
73+
end
74+
end
75+
end
76+
77+
def grand_total
78+
@accounts.map(&:value).inject(0, :+)
79+
end
80+
81+
end
82+
83+
class TransactionalBank
84+
85+
def initialize(account_totals)
86+
@accounts = account_totals.map do |v|
87+
Concurrent::TVar.new(v)
88+
end
89+
end
90+
91+
def transfer(from, to, sum)
92+
Concurrent::atomically do
93+
if @accounts[from].value < sum
94+
false
95+
else
96+
@accounts[from].value -= sum
97+
@accounts[to].value += sum
98+
true
99+
end
100+
end
101+
end
102+
103+
def grand_total
104+
@accounts.map(&:value).inject(0, :+)
105+
end
106+
107+
end
108+
109+
RANDOM = Random.new(0)
110+
111+
Transfer = Struct.new(:from, :to, :sum)
112+
113+
ACCOUNT_TOTALS = (0..100_000).map do
114+
RANDOM.rand(100)
115+
end
116+
117+
GRAND_TOTAL = ACCOUNT_TOTALS.inject(0, :+)
118+
119+
TRANSFERS = (0..1_000_000).map do
120+
Transfer.new(
121+
RANDOM.rand(ACCOUNT_TOTALS.size),
122+
RANDOM.rand(ACCOUNT_TOTALS.size),
123+
RANDOM.rand(100))
124+
end
125+
126+
THREADS = 4
127+
TRANSFER_PER_THREAD = TRANSFERS.size / THREADS
128+
129+
def test(bank_class)
130+
puts bank_class
131+
bank = bank_class.new(ACCOUNT_TOTALS)
132+
133+
puts "total before: #{bank.grand_total}"
134+
135+
start = Time.now
136+
137+
(1..THREADS).map { |n|
138+
Thread.new do
139+
TRANSFERS[(n*TRANSFER_PER_THREAD)..((n+1)*TRANSFER_PER_THREAD)].each do |transfer|
140+
bank.transfer(transfer.from, transfer.to, transfer.sum)
141+
end
142+
end
143+
}.each(&:join)
144+
145+
puts "total after: #{bank.grand_total}"
146+
puts "took #{Time.now - start}s"
147+
end
148+
149+
test UnsynchronizedBank
150+
test CoarseLockBank
151+
test FineLockBank
152+
test TransactionalBank

0 commit comments

Comments
 (0)