ВОЗМОЖНОСТИ RUBY 

в конкурентном
программировании
• Обработка запроса в многопоточном сервере (Puma) 

может быть очень долгим
• Как реализована многопоточность в MRI Ruby
• Как работает планировщик и приоритетность
• Что внутри EventMachine
• Когда вам могут понадобиться файберы
• Дальнейшее развитие многопоточности в Ruby
Многопоточность?
В Ruby?
Global Interpreter Lock
(GIL)
Руби, которые смогли
JRuby
Rubinius
MRI Ruby
thread.c
MRI
model 1: Userlevel Thread
Same as traditional ruby thread.
model 2: Native Thread with Global VM lock
Using pthread and Ruby threads run concurrent.
model 3: Native Thread with fine grain lock
Using pthread and Ruby threads run concurrent or parallel.
YARV Thread Design
MRI
model 1: Userlevel Thread
Same as traditional ruby thread.
model 2: Native Thread with Global VM lock
Using pthread and Ruby threads run concurrent.
model 3: Native Thread with fine grain lock
Using pthread and Ruby threads run concurrent or parallel.
YARV Thread Design
MRI
model 1: Userlevel Thread
Same as traditional ruby thread.
model 2: Native Thread with Global VM lock
Using pthread and Ruby threads run concurrent.
model 3: Native Thread with fine grain lock
Using pthread and Ruby threads run concurrent or parallel.
YARV Thread Design
Стал ли ваш код 

потокобезопасным?
GIL нужен для защиты 

внутреннего состояния MRI
MRI
model 1: Userlevel Thread
Same as traditional ruby thread.
model 2: Native Thread with Global VM lock
Using pthread and Ruby threads run concurrent.
model 3: Native Thread with fine grain lock
Using pthread and Ruby threads run concurrent or parallel.
YARV Thread Design
At Ruby Kaigi 2016, Koichi Sasada
immutable objects can be shared (read) across Guilds
Multi-VM?
GIL
Как устроен GIL?
0.135
fib(29)
• Сборка вьюхи
• Обработка большого количества данных
• …
def show
render text: fib(29)
end
Puma, 5 потоков
$ (curl -i localhost:3000); (curl -i localhost:3000);
HTTP/1.1 200 OK
X-Request-Id: 839cc258-019b-4737-b169-60b05caaceca
X-Runtime: 0.139625
HTTP/1.1 200 OK
X-Request-Id: a3d85a32-53e6-4a5a-99cd-190f6f48f964
X-Runtime: 0.130910
$ (curl -i localhost:3000 &); (curl -i localhost:3000 &);
HTTP/1.1 200 OK
X-Request-Id: 14183c91-7e27-4f7f-be3d-d06814fa6c55
X-Runtime: 0.278702
HTTP/1.1 200 OK
X-Request-Id: 0ff1ed87-3f54-4afd-af34-218601bbafea
X-Runtime: 0.276639
(curl -i localhost:3000 &) x5
1) X-Runtime: 0.682356
2) X-Runtime: 0.682923
3) X-Runtime: 0.481979
4) X-Runtime: 0.412355
5) X-Runtime: 0.414157
Почему так?
и почему именно 0.682356
Thread.new { }
thread_timer(void *p)
while (system_working > 0) {
/* timer function */
ubf_wakeup_all_threads();
timer_thread_function(0);
if (TT_DEBUG) WRITE_CONST(2, "tickn");
/* wait */
timer_thread_sleep(gvl);
}
while (system_working > 0) {
/* timer function */
ubf_wakeup_all_threads();
timer_thread_function(0);
if (TT_DEBUG) WRITE_CONST(2, "tickn");
/* wait */
timer_thread_sleep(gvl);
}
while (system_working > 0) {
/* timer function */
ubf_wakeup_all_threads();
timer_thread_function(0);
if (TT_DEBUG) WRITE_CONST(2, "tickn");
/* wait */
timer_thread_sleep(gvl);
}
timer_thread_function(void *arg)
{
RUBY_VM_SET_TIMER_INTERRUPT(vm->running_thread);
}
while (system_working > 0) {
/* timer function */
ubf_wakeup_all_threads();
timer_thread_function(0);
if (TT_DEBUG) WRITE_CONST(2, "tickn");
/* wait */
timer_thread_sleep(gvl);
}
/* 100ms. 10ms is too small for user level thread scheduling
* on recent Linux (tested on 2.6.35)
*/
#define TIME_QUANTUM_USEC (100 * 1000)
Приоритетность
Thread 1
priority = 1 priority = 0
Thread 2
1
2
3
if (th->priority > 0)
limits_us <<= th->priority;
Else
limits_us >>= -th->priority;
if (th->status == THREAD_RUNNABLE)
th->running_time_us += TIME_QUANTUM_USEC;
rb_thread_schedule_limits(limits_us)
100ms * 2
priority
rb_thread_schedule_limits(unsigned long limits_us)
{
if (th->running_time_us >= limits_us) {
/* … */
}
}
А что если мы хотим сами управлять потоками?
Fiber
For example, on the
* 32bit POSIX OS, ten or twenty thousands Fiber can be created.
• быстрое переключение (смена контекста)
• ручное управление
вы можете написать свой сервер
на файберах и евентмашине
goliath
Goliath is an open source version of the non-blocking (asynchronous) 

Ruby web server framework.
Что такое EventMachine?
(и причем тут файберы)
EM.run do
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p res.response }
end
void EventMachine_t::Run()
{
while (RunOnce()) ;
}
_UpdateTime();
_RunTimers();
_AddNewDescriptors();
_ModifyDescriptors();
_RunEpollOnce();
_DispatchHeartbeats();
_CleanupSockets();
if (bTerminateSignalReceived)
return false;
_UpdateTime();
_RunTimers();
_AddNewDescriptors();
_ModifyDescriptors();
_RunEpollOnce();
_DispatchHeartbeats();
_CleanupSockets();
if (bTerminateSignalReceived)
return false;
_UpdateTime();
_RunTimers();
_AddNewDescriptors();
_ModifyDescriptors();
_RunEpollOnce();
_DispatchHeartbeats();
_CleanupSockets();
if (bTerminateSignalReceived)
return false;
_UpdateTime();
_RunTimers();
_AddNewDescriptors();
_ModifyDescriptors();
_RunEpollOnce();
_DispatchHeartbeats();
_CleanupSockets();
if (bTerminateSignalReceived)
return false;
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
_UpdateTime();
_RunTimers();
_AddNewDescriptors();
_ModifyDescriptors();
_RunEpollOnce();
_DispatchHeartbeats();
_CleanupSockets();
if (bTerminateSignalReceived)
return false;
_UpdateTime();
_RunTimers();
_AddNewDescriptors();
_ModifyDescriptors();
_RunEpollOnce();
_DispatchHeartbeats();
_CleanupSockets();
if (bTerminateSignalReceived)
return false;
_UpdateTime();
_RunTimers();
_AddNewDescriptors();
_ModifyDescriptors();
_RunEpollOnce();
_DispatchHeartbeats();
_CleanupSockets();
if (bTerminateSignalReceived)
return false;
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
EM.run do
fiber = Fiber.new do
current_fiber = Fiber.current
p 1
res = EventMachine::HttpRequest.new("https://www.ruby-lang.org/").aget
res.callback { p 4; current_fiber.resume(5) }
p 2
puts Fiber.yield
p 6
end
p 0
fiber.resume
p 3
end
em-synchrony
Fiber

• yield
• resume
enumerator =
Fiber.new do
val = 0
loop do
val += 1
Fiber.yield(val)
end
end
enumerator.resume # => 1
enumerator.resume # => 2
enumerator.resume # => 3
Вывод
Спасибо
achempion

mri ruby gil