-
Notifications
You must be signed in to change notification settings - Fork 0
/
threading_and_code_execution.html
375 lines (351 loc) · 41.2 KB
/
threading_and_code_execution.html
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
<!DOCTYPE html>
<html prefix="og: http://ogp.me/ns#">
<head>
<meta charset="utf-8">
<meta content="Ruby on Rails, Ruby, Rails, Rails 3, Rails 4, Rails 5, Rails 6, Rails 6.0, Rails 6.1, Rails 7.0, Rails 7.1 руководство, начинающим, самоучитель, manual, мануал, справочник, учебник, примеры, Руби, рельсы" name="keywords"/>
<meta content="Ruby on Rails руководства, учебники, статьи на русском языке" name="description"/>
<meta content="website" property="og:type"/>
<meta content="Rusrails: Треды и выполнение кода в Rails" property="og:title"/>
<meta content="Ruby on Rails руководства, учебники, статьи на русском языке" property="og:description"/>
<meta content="http://localhost:3000/threading_and_code_execution" property="og:url"/>
<meta content="http://rusrails.ru/assets/rusrails.png" property="og:image"/>
<title>
Rusrails: Треды и выполнение кода в Rails
</title>
<link rel="stylesheet" href="/assets/application-f9dfa6ce7fa871006d478e422639671663284ddaa3126cf81ddfe371ac3533c4.css" />
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="/"></a>
<ul class="nav pull-right top-menu">
<li>
<a href="/">Главная</a>
</li>
<li class="dropdown">
<a class="index-popover" href="#">
Содержание
<b class="caret"></b>
</a> <div class="index-popover-content hide">
<ul><li><p><strong>С чего начать?</strong></p><ul><li><a href="/getting-started">Rails для начинающих</a>
</li></ul></li><li><p><strong>Модели</strong></p><ul><li><a href="/active-record-basics">Основы Active Record</a>
</li><li><a href="/active-record-migrations">Миграции Active Record</a>
</li><li><a href="/active-record-validations">Валидации Active Record</a>
</li><li><a href="/active-record-callbacks">Колбэки Active Record</a>
</li><li><a href="/active-record-associations">Связи (ассоциации) Active Record</a>
</li><li><a href="/active-record-querying">Интерфейс запросов Active Record</a>
</li><li><a href="/active-model-basics">Основы Active Model</a>
</li></ul></li><li><p><strong>Вью</strong></p><ul><li><a href="/action-view-overview">Обзор Action View</a>
</li><li><a href="/layouts-and-rendering">Макеты и рендеринг в Rails</a>
</li><li><a href="/action-view-helpers">Хелперы Action View</a>
</li><li><a href="/form-helpers">Хелперы форм в Action View</a>
</li></ul></li><li><p><strong>Контроллеры</strong></p><ul><li><a href="/action-controller-overview">Обзор Action Controller</a>
</li><li><a href="/routing">Роутинг в Rails</a>
</li></ul></li><li><p><strong>Другие компоненты</strong></p><ul><li><a href="/active-support-core-extensions">Расширения ядра Active Support</a>
</li><li><a href="/action-mailer-basics">Основы Action Mailer</a>
</li><li><a href="/action-mailbox-basics">Основы Action Mailbox</a>
</li><li><a href="/action-text-overview">Обзор Action Text</a>
</li><li><a href="/active_job_basics">Основы Active Job</a>
</li><li><a href="/active_storage_overview">Обзор Active Storage</a>
</li><li><a href="/action-cable-overview">Обзор Action Cable</a>
</li></ul></li><li><p><strong>Копаем глубже</strong></p><ul><li><a href="/i18n">API интернационализации Rails (I18n)</a>
</li><li><a href="/testing">Тестирование приложений на Rails</a>
</li><li><a href="/security">Безопасность приложений на Rails</a>
</li><li><a href="/error-reporting">Отчет об ошибках в приложениях Rails</a>
</li><li><a href="/debugging-rails-applications">Отладка приложений на Rails</a>
</li></ul></li></ul><p>next_column</p><ul><li><p><strong>Копаем глубже</strong></p><ul><li><a href="/configuring">Конфигурирование приложений на Rails</a>
</li><li><a href="/command-line">Командная строка Rails</a>
</li><li><a href="/asset-pipeline">Asset Pipeline</a>
</li><li><a href="/working-with-javascript-in-rails">Работа с JavaScript в Rails</a>
</li><li><a href="/initialization">Процесс инициализации в Rails</a>
</li><li><a href="/autoloading-and-reloading-constants">Автозагрузка и перезагрузка констант</a>
</li><li><a href="/caching-with-rails">Кэширование с Rails: Обзор</a>
</li><li><a href="/active-support-instrumentation">Инструментарий Active Support</a>
</li><li><a href="/api-app">Использование Rails для API-приложений</a>
</li><li><a href="/active-record-postgresql">Active Record для PostgreSQL</a>
</li><li><a href="/active-record-multiple-databases">Несколько баз данных с Active Record</a>
</li><li><a href="/active-record-encryption">Шифрование Active Record</a>
</li></ul></li><li><p><strong>Расширяем Rails</strong></p><ul><li><a href="/plugins">Основы создания плагинов Rails</a>
</li><li><a href="/rails-on-rack">Rails on Rack</a>
</li><li><a href="/generators">Создание и настройка генераторов и шаблонов Rails</a>
</li><li><a href="/engines">Engine для начинающих</a>
</li><li><a href="/threading_and_code_execution">Треды и выполнение кода в Rails</a>
</li><li><a href="/rails-application-templates">Шаблоны приложения Rails</a>
</li></ul></li><li><p><strong>Вносим вклад в Ruby on Rails</strong></p><ul><li><a href="/contributing_to_ruby_on_rails">Вносим вклад в Ruby on Rails</a>
</li><li><a href="/api_documentation_guidelines">Рекомендации по документированию API</a>
</li><li><a href="/ruby_on_rails_guides_guidelines">Рекомендации для руководств по Ruby on Rails</a>
</li><li><a href="/development_dependencies_install">Установка зависимостей для разработки</a>
</li><li><a href="/maintenance-policy">Политика поддержки (версий)</a>
</li></ul></li><li><p><strong>Заметки о релизах</strong></p><ul><li><a href="/upgrading-ruby-on-rails">Апгрейд Ruby on Rails</a>
</li><li><a href="/7_2_release_notes">Версия 7.2 - ?</a>
</li><li><a href="/7_1_release_notes">Версия 7.1 - Октябрь 2023</a>
</li><li><a href="/7_0_release_notes">Версия 7.0 - Декабрь 2021</a>
</li><li><a href="/6_1_release_notes">Версия 6.1 - Декабрь 2020</a>
</li><li><a href="/6_0_release_notes">Версия 6.0 - Август 2019</a>
</li></ul></li></ul>
</div>
</li>
<li>
<a href="/search">Поиск</a>
</li>
<li>
<a target="blank" href="http://api.rusrails.ru">Ruby & Rails API</a>
</li>
</ul>
</div>
</div>
</div>
<div class="content-wrapper">
<div class="container-fluid">
<div class="row-fluid">
<div class="span3 pull-right">
<div class="well social">
<h4>Принимаем пожелания и пул-реквесты!</h4>
<iframe allowtransparency="true" frameborder="0" height="30" src="http://ghbtns.com/github-btn.html?user=rusrails&repo=rusrails&type=watch&count=true&size=large" width="180"></iframe>
</div>
<div class="well menu">
<ul class="nav nav-list">
<li>
<h4>
<a href="#avtomaticheskaya-konkurentnost">1. Автоматическая конкурентность</a>
</h4> </li>
<li>
<h4>
<a href="#executor">2. Executor</a>
</h4> </li>
<li>
<h5>
<a href="#defoltnye-kolbeki">2.1. Дефолтные колбэки</a>
</h5> </li>
<li>
<h5>
<a href="#oborachivanie-koda-prilozheniya">2.2. Оборачивание кода приложения</a>
</h5> </li>
<li>
<h5>
<a href="#konkurentnost-concurrency">2.3. Конкурентность (Concurrency)</a>
</h5> </li>
<li>
<h4>
<a href="#reloader">3. Reloader</a>
</h4> </li>
<li>
<h5>
<a href="#kolbeki">3.1. Колбэки</a>
</h5> </li>
<li>
<h5>
<a href="#vygruzka-klassov-class-unload">3.2. Выгрузка классов (Class Unload)</a>
</h5> </li>
<li>
<h5>
<a href="#konkurentnost">3.3. Конкурентность</a>
</h5> </li>
<li>
<h4>
<a href="#povedenie-freymvorka">4. Поведение фреймворка</a>
</h4> </li>
<li>
<h5>
<a href="#konfiguratsiya">4.1. Конфигурация</a>
</h5> </li>
<li>
<h4>
<a href="#load-interlock">5. Load Interlock</a>
</h4> </li>
<li>
<h5>
<a href="#permit_concurrent_loads">5.1. <code>permit_concurrent_loads</code></a>
</h5> </li>
<li>
<h5>
<a href="#actiondispatch-debuglocks">5.2. ActionDispatch::DebugLocks</a>
</h5> </li>
</ul>
</div>
<div class="well banner300 banner">
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- sidebar rusrails -->
<ins class="adsbygoogle"
style="display:inline-block;width:300px;height:600px"
data-ad-client="ca-pub-7764391801669990"
data-ad-slot="6089520660"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
</div>
<div class="span9 content pull-left">
<div class="banner">
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- top rusrails -->
<ins class="adsbygoogle"
style="display:inline-block;width:980px;height:120px"
data-ad-client="ca-pub-7764391801669990"
data-ad-slot="4891989065"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<h2 id='tredy-i-vypolnenie-koda-v-rails' class='inside_page_header'> Треды и выполнение кода в Rails</h2><p>После прочтения этого руководства вы узнаете:</p><ul><li>Какой код Rails будет автоматически выполняться конкурентно
</li><li>Как интегрировать ручную конкурентность с внутренними компонентами Rails
</li><li>Как обернуть весь код приложения
</li><li>Как повлиять на перезагрузку приложения
</li></ul>
<hr>
<h3 id='avtomaticheskaya-konkurentnost' class='inside_page_header'><a href="#avtomaticheskaya-konkurentnost">1.</a> Автоматическая конкурентность</h3><p>Rails автоматически позволяет выполнять различные операции одновременно.</p><p>При использовании тредового веб-сервера, такого как дефолтный Puma, несколько HTTP-запросов будут обслуживаться одновременно, причем каждому запросу предоставляется свой собственный экземпляр контроллера.</p><p>Тредовые адаптеры Active Job, в том числе встроенный Async, будут также выполнять несколько заданий в одно и то же время. Аналогичным образом управляются каналы Action Cable.</p><p>Все эти механизмы связаны с несколькими тредами, каждый из которых управляет работой с уникальным экземпляром некоторого объекта (контроллером, заданием, каналом), разделяя глобальное пространство процесса (например, классов и их конфигураций, и глобальных переменных). Пока код не модифицирует ни одну из этих общих вещей, он может, в основном, игнорировать то, что существуют другие треды.</p><p>В остальной части этого руководства описываются механизмы Rails, используемые чтобы сделать его "по большей части игнорирующим", и как расширения и приложения с особыми потребностями могут использовать их.</p><h3 id='executor' class='inside_page_header'><a href="#executor">2.</a> Executor</h3><p>Executor в Rails отделяет код приложения от кода фреймворка: в любое время, когда фреймворк вызывает код, написанный в приложении, он будет обернут Executor.</p><p>Executor состоит из двух колбэков: <code>to_run</code> и <code>to_complete</code>. Колбэк <code>to_run</code> вызывается до кода приложения, а <code>to_complete</code> - после.</p><h4 id='defoltnye-kolbeki' class='inside_page_header'><a href="#defoltnye-kolbeki">2.1.</a> Дефолтные колбэки</h4><p>В приложении Rails по умолчанию колбэки Executor используются для:</p><ul><li>отслеживания, какие треды находятся в безопасных положениях для автозагрузки и перезагрузки
</li><li>включения и отключения кэша запросов Active Record
</li><li>возвращения приобретенного подключения Active Record к пулу
</li><li>ограничения продолжительности жизни внутреннего кэша
</li></ul><p>До Rails 5.0 некоторые из них обрабатывались отдельными классами промежуточной программы Rack (такими, как <code>ActiveRecord::ConnectionAdapters::ConnectionManagement</code>) или напрямую оборачивали код с помощью таких методов, как <code>ActiveRecord::Base.connection_pool.with_connection</code>. Executor заменяет их с помощью единого, более абстрактного интерфейса.</p><h4 id='oborachivanie-koda-prilozheniya' class='inside_page_header'><a href="#oborachivanie-koda-prilozheniya">2.2.</a> Оборачивание кода приложения</h4><p>Если пишется библиотека или компонент, которые будут вызывать код приложения, необходимо обернуть их с помощью вызова Executor:</p><div class="code_container">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="c1"># здесь вызывается код приложения</span>
<span class="k">end</span>
</code></pre>
</div>
<div class="info"><p>Если повторно вызывается код приложения из долговременного процесса, может потребоваться обернуть его с помощью <a href="#reloader">Reloader</a>.</p></div><p>Каждый тред должен быть обернут до запуска кода приложения, поэтому если приложение вручную делегирует работу другим тредам, например, с помощью <code>Thread.new</code> или функций конкурентного Ruby, которые используют пулы тредов, необходимо немедленно обернуть блок:</p><div class="code_container">
<pre><code class="highlight ruby"><span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="c1"># здесь какой-либо код</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
</div>
<div class="note"><p>Конкурентный Ruby использует <code>ThreadPoolExecutor</code>, который иногда настраивается с помощью опции <code>executor</code>. Несмотря на название, они не связаны.</p></div><p>Executor является безопасно реентерабельным; если он уже активен в текущем треде, <code>wrap</code> игнорируется.</p><p>Если нецелесообразно оборачивать код приложения в блок (например, Rack API делает это проблематично), можно также использовать пару <code>run!</code> / <code>complete!</code>:</p><div class="code_container">
<pre><code class="highlight ruby"><span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="n">execution_context</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">run!</span>
<span class="c1"># здесь какой-либо код</span>
<span class="k">ensure</span>
<span class="n">execution_context</span><span class="p">.</span><span class="nf">complete!</span> <span class="k">if</span> <span class="n">execution_context</span>
<span class="k">end</span>
</code></pre>
</div>
<h4 id='konkurentnost-concurrency' class='inside_page_header'><a href="#konkurentnost-concurrency">2.3.</a> Конкурентность (Concurrency)</h4><p>Executor поместит текущий тред в режим <code>running</code> при <a href="#load-interlock">Load Interlock</a>. Эта операция временно будет блокироваться, если другой тред в настоящее время либо автозагружает константу, либо выгружает/перезагружает приложение.</p><h3 id='reloader' class='inside_page_header'><a href="#reloader">3.</a> Reloader</h3><p>Как и Executor, Reloader также оборачивает код приложения. Если Executor не активен в текущем треде, Reloader вызовет его сам, поэтому необходимо вызывать только Reloader. Это также гарантирует, что все, что делает Reloader, включая все его вызовы колбэка, оказывается обернутым внутри Executor.</p><div class="code_container">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">reloader</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="c1"># здесь вызывается код приложения</span>
<span class="k">end</span>
</code></pre>
</div>
<p>Reloader подходит только тогда, когда долговременный процесс на уровне фреймворка повторно вызывается в коде приложения, например, для веб-сервера или очереди заданий. Rails автоматически оборачивает веб-запросы и воркеры Active Job, поэтому редко приходится ссылаться на Reloader. Всегда учитывайте, подходит ли Executor для конкретного случая.</p><h4 id='kolbeki' class='inside_page_header'><a href="#kolbeki">3.1.</a> Колбэки</h4><p>Перед входом в обернутый блок Reloader проверит, нужно ли перезагружать запущенное приложение - например, потому что исходный файл модели был модифицирован. Если он определяет, что требуется перезагрузка, он будет ждать до тех пор, пока это будет безопасно, а затем сделает это, прежде чем продолжить. Когда приложение настроено на постоянную перезагрузку независимо от того, обнаружены ли какие-либо изменения, перезагрузка будет выполняется в конце блока.</p><p>Reloader также предоставляет колбэки <code>to_run</code> и <code>to_complete</code>; они вызываются в тех же точках, что и для Executor, но только когда текущее выполнение инициировало перезагрузку приложения. Когда нет необходимости в перезагрузке, Reloader будет вызывать обернутый блок без каких-либо других колбэков.</p><h4 id='vygruzka-klassov-class-unload' class='inside_page_header'><a href="#vygruzka-klassov-class-unload">3.2.</a> Выгрузка классов (Class Unload)</h4><p>Наиболее значительная часть процесса перезагрузки - это выгрузка классов, в которой все автозагруженные классы удаляются, и их можно снова загружать. Это происходит непосредственно до колбэков <code>to_run</code> или <code>to_complete</code>, в зависимости от параметра <code>reload_classes_only_on_change</code>.</p><p>Зачастую дополнительные экшны по перезагрузке должны выполняться как непосредственно до, так и сразу после выгрузки классов, поэтому Reloader также предоставляет колбэки <code>before_class_unload</code> и <code>after_class_unload</code>.</p><h4 id='konkurentnost' class='inside_page_header'><a href="#konkurentnost">3.3.</a> Конкурентность</h4><p>Только долговременные процессы "верхнего уровня" должны ссылаться на Reloader, потому что если он определяет, что требуется перезагрузка, он будет блокироваться до тех пор, пока все другие треды не завершат любые вызовы Executor.</p><p>Если это произойдет в "дочернем" треде, с ожидающим родителем внутри Executor, это вызовет неизбежный дедлок: перезагрузка должна произойти до того, как дочерний тред будет выполнен, но он не может быть безопасно выполнен, пока родительский тред находится в середине выполнения. Вместо этого дочерний тред должен использовать Executor.</p><h3 id='povedenie-freymvorka' class='inside_page_header'><a href="#povedenie-freymvorka">4.</a> Поведение фреймворка</h3><p>Компоненты фреймворка Rails используют эти инструменты для управления своей собственной конкурентностью.</p><p><code>ActionDispatch::Executor</code> и <code>ActionDispatch::Reloader</code> являются промежуточными программами Rack, которые обертывают запросы с помощью поставляемого Executor или Reloader, соответственно. Они автоматически включены в дефолтный стек приложений. Reloader гарантирует, что любой входящий HTTP-запрос будет обслуживаться последней копией приложения, если произойдут какие-либо изменения кода.</p><p>Active Job также оборачивает выполнение задания с помощью Reloader, загружая последний код для выполнения каждого задания, когда оно выходит из очереди.</p><p>Action Cable использует вместо этого Executor: поскольку соединение Cable связано с конкретным экземпляром класса, его невозможно перезагрузить для каждого прибывающего сообщения WebSocket. Тем не менее, обрабатывается только обработчик сообщений; долговременное соединение Cable не предотвращает перезагрузку, вызванную новым входящим запросом или заданием. Вместо этого Action Cable использует колбэк <code>before_class_unload</code> Reloader для отключения всех его соединений. Когда клиент автоматически переподключается, он имеет дело с новой версией кода.</p><p>Вышеупомянутые являются точками входа в фреймворк, поэтому они несут ответственность за обеспечение своих соответствующих тредов и принятие решения о необходимости перезагрузки. Другие компоненты должны использовать Executor, только когда они порождают дополнительные треды.</p><h4 id='konfiguratsiya' class='inside_page_header'><a href="#konfiguratsiya">4.1.</a> Конфигурация</h4><p>Reloader проверяет только изменения файлов, когда <code>config.enable_reloading</code> это <code>true</code>, как и <code>config.reload_classes_only_on_change</code>. Это значения по умолчанию в среде <code>development</code>.</p><p>Когда <code>config.enable_reloading</code> это <code>false</code> (в <code>production</code>, по умолчанию), Reloader это всего лишь переходник к Executor.</p><p>У Executor всегда есть важная работа, например управление подключением к базе данных. Когда <code>config.enable_reloading</code> это <code>false</code> и <code>config.eager_load</code> это <code>true</code> (по умолчанию в <code>production</code>), перезагрузка класса не будут происходить, поэтому Load Interlock не требуется. С настройками по умолчанию в <code>development</code> Executor будет использовать Load Interlock, гарантируя что константы загружаются только тогда, когда это безопасно.</p><h3 id='load-interlock' class='inside_page_header'><a href="#load-interlock">5.</a> Load Interlock</h3><p>Load Interlock позволяет включить автозагрузку и перезагрузку в многотредовой среде выполнения.</p><p>Когда один тред выполняет автозагрузку, оценивая определение класса из соответствующего файла, важно чтобы никакой другой тред не встречал обращение к частично определенной константе.</p><p>Точно так же безопасно выполнять выгрузку/перезагрузку, когда код приложения не находится в середине выполнения: после перезагрузки константа <code>User</code>, например, может указывать на другой класс. Без этого правила несвоевременная перезагрузка будет означать, что <code>User.new.class == User</code> или даже <code>User == User</code> могут быть false.</p><p>Оба этих ограничения рассматриваются Load Interlock. Он отслеживает, какие треды в настоящее время запускают код приложения, загружают класс или выгружают автозагруженные константы.</p><p>Одновременно только один тред может загружать или выгружать, а сделав это, он должен подождать пока другие треды не будут запускать код приложения. Если тред ожидает выполнения загрузки, он не предотвращает загрузку других тредов (на самом деле, они будут взаимодействовать, и каждый из них будет выполнять свою постановку в очередь, прежде чем все вместе возобновится).</p><h4 id='permit_concurrent_loads' class='inside_page_header'><a href="#permit_concurrent_loads">5.1.</a> <code>permit_concurrent_loads</code></h4><p>Executor автоматически приобретает блокировку <code>running</code> на протяжении всего своего блока, и автозагрузка знает, когда производить апгрейд блокировки <code>load</code>, и снова вернуться к <code>running</code>.</p><p>Однако другие блокирующие операции, выполняемые внутри блока Executor (включая весь код приложения), могут без необходимости удерживать блокировку <code>running</code>. Если другой тред встречает константу, он должен автозагружаться, это может вызвать дедлок.</p><p>Например, если предположить, что <code>User</code> еще не загружен, следующее приведет к дедлоку:</p><div class="code_container">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="n">th</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="no">User</span> <span class="c1"># внутренний тред ждет здесь; он не может</span>
<span class="c1"># загружать User, пока выполняется другой тред</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">th</span><span class="p">.</span><span class="nf">join</span> <span class="c1"># внешний тред ждет здесь, удерживая блокировку 'running'</span>
<span class="k">end</span>
</code></pre>
</div>
<p>Чтобы предотвратить этот дедлок, внешний тред может <code>permit_concurrent_loads</code>. Вызывая этот метод, тред гарантирует, что он не будет разыменовывать любую потенциально автозагруженную константу внутри предоставленного блока. Самый безопасный способ выполнить это обещание - максимально приблизить его к блокирующему вызову:</p><div class="code_container">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="n">th</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="no">User</span> <span class="c1"># внутренний тред может приобрести блокировку 'load',</span>
<span class="c1"># загрузить User и продолжить</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Dependencies</span><span class="p">.</span><span class="nf">interlock</span><span class="p">.</span><span class="nf">permit_concurrent_loads</span> <span class="k">do</span>
<span class="n">th</span><span class="p">.</span><span class="nf">join</span> <span class="c1"># внешний тред ждет здесь, но не имеет блокировки</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
</div>
<p>Другой пример, с использованием конкурентного Ruby:</p><div class="code_container">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="n">futures</span> <span class="o">=</span> <span class="mi">3</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">collect</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="no">Concurrent</span><span class="o">::</span><span class="no">Promises</span><span class="p">.</span><span class="nf">future</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="c1"># здесь делаем работу</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">values</span> <span class="o">=</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Dependencies</span><span class="p">.</span><span class="nf">interlock</span><span class="p">.</span><span class="nf">permit_concurrent_loads</span> <span class="k">do</span>
<span class="n">futures</span><span class="p">.</span><span class="nf">collect</span><span class="p">(</span><span class="o">&</span><span class="ss">:value</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
</div>
<h4 id='actiondispatch-debuglocks' class='inside_page_header'><a href="#actiondispatch-debuglocks">5.2.</a> ActionDispatch::DebugLocks</h4><p>Если приложение попадает в дедлок, и вы думаете, что это из-за Load Interlock, можно временно добавить промежуточную программу ActionDispatch::DebugLocks в <code>config/application.rb</code>:</p><div class="code_container">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">middleware</span><span class="p">.</span><span class="nf">insert_before</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Sendfile</span><span class="p">,</span>
<span class="no">ActionDispatch</span><span class="o">::</span><span class="no">DebugLocks</span>
</code></pre>
</div>
<p>Если затем перезапустить приложение и перезапустить условие дедлока, <code>/rails/locks</code> покажет сводку всех тредов, которые в настоящее время известны интерлоку, какой уровень блокировки они удерживают или ждут, и их текущий бэктрейс.</p><p>Как правило, дедлок будет вызван интерлоком, конфликтующим с какой-либо другой внешней блокировкой или блокирующим I/O вызовом. Как только он будет найден, можно обернуть его с помощью <code>permit_concurrent_loads</code>.</p>
<div class="banner">
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- bottom rusrails -->
<ins class="adsbygoogle"
style="display:inline-block;width:580px;height:400px"
data-ad-client="ca-pub-7764391801669990"
data-ad-slot="7566253867"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12" id="footer">
<p>
<a target="blank" href="https://github.com/rusrails/rusrails"><img src="/assets/github-7cc23602a5ac2465f14c19492358a5a67dc24636761cc723e4d621cea0c09225.png" /></a>
</p>
<p>
<a href="https://creativecommons.org/licenses/by-sa/4.0/">Лицензия CC BY-SA 4.0</a>
"Rails", "Ruby on Rails" и логотип Rails - торговые марки DHH
<!-- Yandex.Metrika counter -->
<script>
(function (d, w, c) {
(w[c] = w[c] || []).push(function() {
try {
w.yaCounter1006929 = new Ya.Metrika({id:1006929,
webvisor:true,
clickmap:true,
trackLinks:true,
accurateTrackBounce:true});
} catch(e) { }
});
var n = d.getElementsByTagName("script")[0],
s = d.createElement("script"),
f = function () { n.parentNode.insertBefore(s, n); };
s.type = "text/javascript";
s.async = true;
s.src = (d.location.protocol == "https:" ? "https:" : "http:") + "//mc.yandex.ru/metrika/watch.js";
if (w.opera == "[object Opera]") {
d.addEventListener("DOMContentLoaded", f, false);
} else { f(); }
})(document, window, "yandex_metrika_callbacks");
</script>
<noscript>
<div>
<img style="position:absolute; left:-9999px;" alt="" src="//mc.yandex.ru/watch/1006929" />
</div>
</noscript>
<!-- /Yandex.Metrika counter -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-64955373-1', 'auto');
ga('send', 'pageview');
var trackOutboundLink = function(url) {
ga('send', 'event', 'outbound', 'click', url, {
'transport': 'beacon',
'hitCallback': function(){ }
});
}
</script>
</p>
</div>
</div>
</div>
</div>
<div class="to_top" style="display: block">
<div class="to_top_panel"></div>
</div>
<script src="/assets/application-48ac5c5be8858f5558a99606c2d341f9fee482f22db6deee5def03837c505584.js"></script>
</body>
</html>