Книжная полка Сохранить
Размер шрифта:
А
А
А
|  Шрифт:
Arial
Times
|  Интервал:
Стандартный
Средний
Большой
|  Цвет сайта:
Ц
Ц
Ц
Ц
Ц

Семь моделей конкуренции и параллелизма за семь недель. Раскрываем тайны потоков

Покупка
Артикул: 816355.01.99
Доступ онлайн
559 ₽
В корзину
С появлением микропроцессоров, обладающих большим числом ядер, понимание конкуренции и параллелизма при разработке программного обеспечения стало еще более важным, чем прежде. В книге вы познакомитесь с преимуществами функционального программирования с точки зрения конкуренции, узнаете, как применять акторы для разработки распределенного программного обеспечения, и исследуете приемы параллельной обработки огромных объемов информации на нескольких процессорах. Эта книга поможет вам приобрести новые навыки в разработке программ, благодаря чему вы будете готовы решать сложные задачи в ближайшие несколько лет.
Батчер, П. Семь моделей конкуренции и параллелизма за семь недель. Раскрываем тайны потоков : практическое руководство / П. Батчер ; пер. с англ. А. Н. Киселева. - 2-е изд. - Москва : ДМК Пресс, 2023. - 362 с. - ISBN 978-5-89818-332-5. - Текст : электронный. - URL: https://znanium.ru/catalog/product/2102619 (дата обращения: 09.05.2024). – Режим доступа: по подписке.
Фрагмент текстового слоя документа размещен для индексирующих роботов. Для полноценной работы с документом, пожалуйста, перейдите в ридер.
Семь моделей  
конкуренции  
и параллелизма 
за Семь недель

РаскРываем тайны потоков

автор серии: пол Батчер

редактор-консультант: Жаклин картер
When Threads Unravel

Paul Butcher

Seven  
Concurrency Models 
in Seven Weeks

Dallas, Texas • Raleigh, NoRTh CaRoliNa
Москва, 2023

Раскрываем тайны потоков

Пол Батчер

Семь моделей 
конкуренции  
и параллелизма 
за семь недель

2-е издание, электронное
УДК 004.42
ББК 32.973
Б28

Б28
Батчер, Пол.
Семь моделей конкуренции и параллелизма за семь недель. Раскрываем тайны 
потоков / П. Батчер ; пер. с англ. А. Н. Киселева. — 2-е изд., эл. — 1 файл pdf : 
362 с. — Москва : ДМК Пресс, 2023. — (The Pragmatic Programmers). — Систем. 
требования: Adobe Reader XI либо Adobe Digital Editions 4.5 ; экран 10". — Текст : 
электронный.
ISBN 978-5-89818-332-5

С появлением микропроцессоров, обладающих большим числом ядер, понимание конкуренции 
и параллелизма при разработке программного обеспечения стало еще более важным, 
чем прежде. В книге вы познакомитесь с преимуществами функционального программирования 
с точки зрения конкуренции, узнаете, как применять акторы для разработки 
распределенного программного обеспечения, и исследуете приемы параллельной обработки 
огромных объемов информации на нескольких процессорах. Эта книга поможет вам 
приобрести новые навыки в разработке программ, благодаря чему вы будете готовы решать 
сложные задачи в ближайшие несколько лет.

УДК 004.42 
ББК 32.973

Электронное издание на основе печатного издания: Семь моделей конкуренции и параллелизма за семь 
недель. Раскрываем тайны потоков / П. Батчер ; пер. с англ. А. Н. Киселева. — Москва : ДМК Пресс, 
2015. — 360 с. — (The Pragmatic Programmers). — ISBN 978-5-97060-244-7. — Текст : непосредственный.

Все права защищены. Любая часть этой книги не может быть воспроизведена в какой бы то ни было форме и 
какими бы то ни было средствами без  письменного разрешения владельцев авторских прав.
Материал, изложенный в данной книге, многократно проверен. Но поскольку вероятность технических ошибок 
все равно существует, издательство не может гарантировать абсолютную точность и правильность приводимых 
сведений. В связи с этим издательство не несет ответственности за возможные ошибки, связанные с использованием 
книги.

В соответствии со ст. 1299 и 1301 ГК РФ при устранении ограничений, установленных техническими средствами защиты 
авторских прав, правообладатель вправе требовать от нарушителя возмещения убытков или выплаты компенсации.

ISBN 978-5-89818-332-5
© 2014 The Pragmatic Programmers, LLC.
©  Оформление, перевод на русский язык 
ДМК Пресс, 2015
Положительные отзывы 
к книге «Семь моделей 
конкуренции и параллелизма 
за семь недель»

Уже не одно десятилетие профессиональные программисты реализуют 
параллельное выполнение кода с применением потоков и блокировок. Но 
эта модель – лишь одна из многих, что ярко демонстрирует книга «Семь 
моделей конкуренции и параллелизма за семь недель». Если вы хотите добиться 
успеха в мире, где основные языки программирования постепенно 
обретают поддержку акторов, CSP1, параллелизма данных, функционального 
программирования и заимствуют унифицированную модель последовательностей 
из языка Clojure, обязательно прочитайте эту книгу.

➤ Стюарт Хэллоуэй (Stuart Halloway)
Сооснователь, Cognitect
С появлением микропроцессоров, обладающих большим числом ядер, 
понимание конкуренции стало еще более важным, чем прежде. Вы познакомитесь 
с преимуществами функционального программирования с 
точки зрения конкуренции, узнаете, как применять акторы для разработки 
распределенного программного обеспечения, и исследуете приемы 
параллельной обработки огромных объемов информации на нескольких 
процессорах. Эта книга поможет вам приобрести новые навыки в разработке 
программ, благодаря чему вы будете готовы решать сложные задачи 
в ближайшие несколько лет.

➤ Хосе Валим (José Valim)
Сооснователь Plataformatec
В книге «Семь моделей конкуренции и параллелизма за семь недель», 
которая является поучительным обзором различных приемов параллельного/
конкурентного выполнения, автору удалось найти баланс между 
описанием скучной теории и живыми экспериментами.

➤ Фредерик Чанг (Frederick Cheung)
Технический директор, Dressipi
Мир постоянно изменяется, и каждый действующий программист 
должен изучать приемы конкурентного программирования. Теперь, ког-

1 
Communicating sequential processes – взаимодействующие последовательные процессы. – 
Прим. перев.
да меня будут спрашивать: «Как сделать это?», – я смогу предложить 
прочитать эту книгу. Я многое узнал из нее и с радостью могу порекомендовать 
ее.

➤ Эндрю Хейли (Andrew Haley)
Ведущий программист на Java, Red Hat
По мере того, как закон Адмала2 начинает преобладать над законом 
Мура3, происходит переход от объектно-ориентированного к конкурентно-
ориентированному программированию. Вследствие этого данная книга 
появилась как нельзя кстати. Пол проделал фантастическую работу, 
описав наиболее важные модели конкуренции и дав читателю обширный 
набор инструментов, из которого он сможет выбирать наиболее подходящие 
для его потребностей. Эту книгу должен прочитать каждый, кто 
разрабатывает программное обеспечение в нашу многоядерную эпоху.

➤ Франческо Цезарини (Francesco Cesarini)
Основатель и технический директор Erlang Solutions
В своей книге Пол дает превосходное введение в тернистую тему конкуренции 
и параллелизма, ясно и доходчиво описывая различные подходы.

➤ 
Шон Эллис (Sean Ellis)
Архитектор GPU, ARM
Просто о сложном. Я бы с удовольствием прослушал университетский 
курс по этой теме, взяв в качестве руководства «Семь моделей конкуренции 
и параллелизма за семь недель».

➤ Карлос Сесса (Carlos Sessa)
Разработчик для Android, Groupon
Пол Батчер (Paul Butcher) взял проблему, которая бросает в дрожь 
многих разработчиков, и наглядно продемонстрировал целую серию парадигм 
программирования, которые с успехом можно использовать для 
реализации конкуренции в программном обеспечении.

➤ Пайди Крид (Páidí Creed)
Программист, SwiftKey
Имея опыт сотрудничества с Полом, могу рекомендовать его как опытного 
эксперта в области архитектуры языков программирования. Эта 
книга является ясным описанием темы, часто недооцененной, но жизненно 
важной для разработки программного обеспечения.

➤ Бен Медлок (Ben Medlock)
Сооснователь и технический директор, SwiftKey

2 
https://ru.wikipedia.org/wiki/Закон_Адмала – Прим. перев.
3 
https://ru.wikipedia.org/wiki/Закон_Мура – Прим. перев.

Положительные отзывы к книге … 
оглавление

положительные отзывы к книге «семь моделей 
конкуренции и параллелизма за семь недель» ...........5
предисловие ................................................... 13
Благодарности ................................................. 15
вступление ..................................................... 17

О книге ...........................................................................................17
Чем не является эта книга...............................................................18
Примеры кода ................................................................................18
Примечание для пользователей IDE................................................19
Примечание для пользователей Windows ........................................19
Ресурсы в Сети ...............................................................................19

Глава 1. введение ............................................. 21

Конкуренция или параллелизм? .............................................. 21
Похожие, но разные ........................................................................21
За рамками последовательного программирования .......................23
Параллельная архитектура ...................................................... 23
Параллелизм на уровне битов ........................................................24
Параллелизм на уровне инструкций ...............................................24
Параллелизм данных ......................................................................24
Параллелизм на уровне задач ........................................................25
Конкуренция: за рамками множества ядер .............................. 26
Конкурентные программы для конкурентного мира ........................26
Распределенные программы для распределенного мира ...............27
Надежные программы для непредсказуемого мира ........................27
Простые программы в сложном мире .............................................28
Семь моделей ......................................................................... 28

Глава 2. потоки выполнения и блокировки ............ 31

Самое простое из того, что может работать............................. 31
День 1: взаимоисключение и модели памяти ........................... 32
Создание потока ............................................................................33
Наша первая блокировка ................................................................34
Загадочная память .........................................................................37
Оглавление

Видимость памяти ..........................................................................38
Несколько блокировок ....................................................................39
Опасности сторонних методов .......................................................43
В завершение первого дня .............................................................44
День 2: помимо встроенных блокировок .................................. 46
Прерываемое блокирование ..........................................................47
Тайм-ауты .......................................................................................49
Блокирование методом перебора ..................................................51
Условные переменные ....................................................................54
Атомарные переменные .................................................................57
В завершение второго дня ..............................................................58
День 3: на плечах гигантов ....................................................... 60
Еще раз о создании потоков ...........................................................61
Копирование при записи ................................................................62
Законченная программа .................................................................64
В завершение третьего дня ............................................................74
В завершение .......................................................................... 75
Сильные стороны ...........................................................................75
Слабые стороны .............................................................................76
Другие языки ..................................................................................78
Напоследок ....................................................................................79

Глава 3. Функциональное программирование ........ 80

Если какие-то действия вредят вам, перестаньте 
выполнять их ........................................................................... 80
День 1: программирование без изменяемого состояния ......... 81
Опасности изменяемого состояния ................................................81
Краткий экскурс в язык Clojure ........................................................84
Первая функциональная программа ...............................................86
Параллелизм без усилий ................................................................87
Функциональный подсчет слов .......................................................89
Лень – это благо .............................................................................93
В завершение первого дня .............................................................94
День 2: функциональный параллелизм .................................... 95
По одной странице за раз ...............................................................95
Разделение данных на пакеты для увеличения  
производительности ......................................................................98
Редуценты (reducers) ......................................................................99
Внутреннее устройство редуцентов ..............................................100
Разделяй и властвуй .....................................................................103
Поддержка функции fold ...............................................................104
Подсчет слов с помощью fold ........................................................105
В завершение второго дня ............................................................107
День 3: функциональная конкуренция .................................... 108
Та же структура, разный порядок вычислений ...............................108
Оглавление

Ссылочная прозрачность ..............................................................109
Потоки данных ..............................................................................110
Механизм future ............................................................................111
Механизм promise ........................................................................112
Функциональная веб-служба ........................................................113
В завершение третьего дня ..........................................................121
В завершение ........................................................................ 122
Сильные стороны .........................................................................124
Слабые стороны ...........................................................................124
Другие языки ................................................................................124
Напоследок ..................................................................................125

Глава 4. путь Clojure – разделение идентичности 
и состояния ................................................... 126

Лучшее из двух миров ........................................................... 126
День 1: атомы и сохранные структуры данных ....................... 127
Атомы ...........................................................................................127
Многопоточная веб-служба с изменяемым состоянием ...............129
Сохранные структуры данных .......................................................130
Идентичность или состояние ........................................................134
Повторения ..................................................................................134
Валидаторы ..................................................................................135
Функции-наблюдатели .................................................................135
Гибридная веб-служба ..................................................................136
В завершение первого дня ...........................................................140
День 2: агенты и программная транзакционная память ......... 141
Агенты ..........................................................................................141
Журнал в памяти ...........................................................................145
Программная транзакционная память ..........................................146
Изменяемое разделяемое состояние ...........................................151
В завершение второго дня ............................................................151
День 3: погружение в глубину ................................................ 152
Решение задачи о философах на основе STM ...............................153
Решение задачи о философах без применения STM .....................155
Атомы или STM? ...........................................................................157
Собственная реализация конкуренции .........................................158
В завершение третьего дня ..........................................................160
В завершение ........................................................................ 161
Сильные стороны .........................................................................161
Слабые стороны ...........................................................................162
Другие языки ................................................................................162
Напоследок ..................................................................................162

Глава 5. акторы .............................................. 164

Не объекты, а скорее ориентированные на объекты .............. 164
Оглавление

День 1: сообщения и почтовые ящики .................................... 166
Наш первый актор ........................................................................166
Почтовые ящики и очереди ...........................................................167
Прием сообщений ........................................................................168
Связывание процессов .................................................................169
Акторы с сохранением состояния .................................................170
Сокрытие сообщений за фасадом API ...........................................171
Двунаправленное взаимодействие ...............................................172
Именование процессов ................................................................174
Отступление – функции первого порядка......................................176
Параллельная версия map() ..........................................................176
В завершение первого дня ...........................................................177
День 2: обработка ошибок и отказоустойчивость ................... 178
Актор кэширования ......................................................................179
Определение момента отказа .......................................................182
Слежение за работой процессов ..................................................185
Тайм-ауты .....................................................................................186
Ядро ошибки ................................................................................187
И пусть падает! .............................................................................189
В завершение второго дня ............................................................190
День 3: распределенные приложения .................................... 191
OTP ..............................................................................................191
Узлы .............................................................................................196
Распределенный счетчик слов ......................................................200
В завершение третьего дня ..........................................................206
В завершение ........................................................................ 207
Сильные стороны .........................................................................208
Слабые стороны ...........................................................................209
Другие языки ................................................................................209
Напоследок ..................................................................................210

Глава 6. взаимодействие последовательных 
процессов ..................................................... 211

Взаимодействия – это все ..................................................... 211
День 1: каналы и блоки go ...................................................... 213
Каналы .........................................................................................213
Блоки go .......................................................................................217
Операции с каналами ...................................................................222
В завершение первого дня ...........................................................225
День 2: множество каналов и ввод/вывод .............................. 227
Обслуживание множества каналов ...............................................227
Асинхронный опрос ......................................................................230
Асинхронный ввод/вывод .............................................................233
В завершение второго дня ............................................................240
День 3: модель CSP на стороне клиента ................................ 241
Оглавление

Конкуренция – это образ жизни ....................................................242
Привет, ClojureScript .....................................................................242
Обработка событий ......................................................................245
Усмирение функций обратного вызова .........................................247
Отправляемся в путь, чтобы увидеть Мастера ..............................248
В завершение третьего дня ..........................................................251
В завершение ........................................................................ 251
Сильные стороны .........................................................................252
Слабые стороны ...........................................................................252
Другие языки ................................................................................253
Напоследок ..................................................................................253

Глава 7. параллелизм данных ........................... 254

В недрах вашего ноутбука спрятан суперкомпьютер.............. 254
День 1: программирование GPGPU ........................................ 255
Обработка данных и параллелизм данных ....................................255
Наша первая программа для OpenCL ............................................258
Профилирование ..........................................................................264
Множество возвращаемых значений ............................................265
Обработка ошибок ........................................................................266
В завершение первого дня ...........................................................268
День 2: многомерность и рабочие группы .............................. 270
Многомерные массивы рабочих элементов ..................................270
Получение информации об устройстве .........................................273
Модель платформы ......................................................................275
Модель памяти .............................................................................276
Параллельная свертка ..................................................................277
Свертка с одной рабочей группой .................................................277
В завершение второго дня ............................................................282
День 3: OpenCL и OpenGL – храните данные в GPU ................ 283
Водная рябь .................................................................................284
LWJGL ...........................................................................................284
Отображение сетки в OpenGL .......................................................285
Доступ к буферу OpenGL из ядра OpenCL .....................................287
Имитация ряби .............................................................................288
В завершение третьего дня ..........................................................291
В завершение ........................................................................ 293
Сильные стороны .........................................................................293
Слабые стороны ...........................................................................294
Другие языки ................................................................................294
Напоследок ..................................................................................294

Глава 8. Лямбда-архитектура ........................... 295

Параллелизм позволяет обрабатывать гигантские объемы 
данных................................................................................... 295
Оглавление

День 1: MapReduce ................................................................ 297
Практические аспекты ..................................................................298
Основы Hadoop ............................................................................299
Подсчет слов с помощью Hadoop .................................................301
Опробование на Amazon EMR .......................................................305
Обработка XML .............................................................................308
В завершение первого дня ...........................................................310
День 2: пакетный уровень ...................................................... 313
Проблемы с традиционными системами данных ..........................313
Вечные истины .............................................................................315
Лучшие данные – исходные данные ..............................................315
Авторы правок в Википедии ..........................................................317
Завершение картины ....................................................................323
В завершение второго дня ............................................................325
День 3: уровень ускорения .................................................... 327
Архитектура уровня ускорения .....................................................328
Подсчет правок с помощью Storm .................................................335
В завершение третьего дня ..........................................................341
В завершение ........................................................................ 342
Сильные стороны .........................................................................343
Слабые стороны ...........................................................................343
Альтернативы ...............................................................................343
Напоследок ..................................................................................343

Глава 9. в заключение ..................................... 344

Куда мы идем? ....................................................................... 344
Будущее за неизменяемостью ......................................................345
Будущее за распределенными вычислениями ..............................346
Темы, оставшиеся за бортом ................................................. 346
Fork/Join и захват задачи ..............................................................347
Потоки данных  .............................................................................347
Реактивное программирование ....................................................347
Функциональное реактивное программирование .........................348
Grid-вычисления ...........................................................................348
Пространства кортежей ................................................................348
Выбор за вами ....................................................................... 349

Библиография ............................................... 350

предметный указатель .................................... 352
ПредиСловие

Эта книга рассказывает историю.
Кому-то может показаться странным, что книга начинается с этих 
слов, но для меня они наполнены смыслом. Видите ли, при создании 
книги «Семь моделей конкуренции и параллелизма за семь недель» 
мы отклонили десятки предложений от разных авторов, считавших, 
что можно собрать в кучу семь не связанных между собой статей и 
назвать это книгой. Мы думаем иначе.
В первой нашей истории («Seven Languages in Seven Weeks: 
A Pragmatic Guide to Learning Programming Languages» [Tat10]) рассказывалось, 
что объектно-ориентированные языки были хороши для 
своего времени, но под давлением всевозрастающей сложности программного 
обеспечения и необходимости управления конкуренцией 
в многоядерных архитектурах стали появляться языки функционального 
программирования, формирующие иные подходы к программированию. 
Пол Батчер (Paul Butcher) оказался самым полезным из 
научных редакторов этой книги. После четырехлетнего знакомства я 
понял, почему.
Пол многое повидал, находясь в первых рядах тех, кто внедрял по-
настоящему масштабируемую поддержку конкуренции в действующие 
приложения. В книге «Seven Languages» он подметил, что решение 
многих сложных задач все чаще и чаще переносится на уровень 
языка. Пару лет спустя Пол пришел к нам с предложением написать 
свою книгу. Он говорил, что языки занимают важное место в общей 
истории, но они – лишь верхушка айсберга. Он хотел поведать нашим 
читателям намного более полную историю и простым языком 
рассказать о наиболее важных инструментах, которые используются 
в современном программном обеспечении для решения проблем, связанных 
с параллельным выполнением кода.
Сначала мы скептически отнеслись к его предложению. Такие книги 
пишутся тяжело – для них требуется больше времени, а процент 
неудач слишком высок – Пол решил сразиться со слишком большим 
драконом. Но, став настоящей командой, мы смогли превратить пер-
воначальный список глав в хорошую историю. По мере того как росла 
стопка страниц, мы все отчетливее понимали, что Пол не только 
имел обширные технические знания для раскрытия данной темы, но 
и азарт, чтобы взяться за нее. Мы также осознали всю особенность 
этой книги и ее своевременность. По мере углубления в нее вы поймете, 
о чем я говорю.
Вы будете ежиться вместе с нами, читая главу о потоках выполнения 
и блокировках – наиболее широко используемых ныне механизмах 
реализации конкуренции. Вы увидите, в каких ситуациях это 
решение дает плохие результаты, и затем приметесь за работу. Пол познакомит 
вас с самыми разными подходами, от лямбда-архитектуры, 
используемой в некоторых высоконагруженных социальных платформах, 
до модели акторов, лежащей в основе многих крупнейших в 
мире, высоконадежных телекоммуникационных систем. Вы увидите 
языки программирования, которыми пользуются профессионалы, от 
Java до Clojure и захватывающего языка Elixir, основанного на Erlang. 
И на каждом шаге этого сложного пути Пол будет поддерживать вас, 
делясь своими глубокими знаниями посвященного.
Я рад представить вам книгу «Семь моделей конкуренции и параллелизма 
за семь недель» и надеюсь, что для вас она окажется такой же 
ценной, как и для меня.

Брюс Тейт (Bruce A. Tate)
Технический директор icanmakeitbetter.com
Редактор серии «Семь за семь» 
Остин, Техас

Предисловие
БлагодарноСти

Когда я объявил, что подписал контракт на создание этой книги, друг 
спросил меня: «Как, прошло столько времени, что ты забыл, как тебе 
далась первая книга?» Я был слишком наивен, полагая, что работать 
над второй книгой будет проще. Возможно, если бы я выбрал иной 
формат, отличный от формата серии «Семь за семь», я был бы не так 
уж и не прав.
Я едва ли смог бы написать эту книгу без мощной поддержки редактора 
серии Брюса Тейта и редактора-консультанта Джеки Картер 
(Jackie Carter). Спасибо вам обоим, что поддерживали меня, пока я 
вынашивал эту книгу, а также Дейву и Энди за возможность еще раз 
внести свой вклад в эту великолепную серию.
Многие делились со мной советами во время работы над рукописью, 
в том числе (без какого-то определенного порядка): Симон Харди-
Френсис (Simon Hardy-Francis), Сэм Холлидей (Sam Halliday), Майк 
Смит (Mike Smith), Нил Эклс (Neil Eccles), Мэтью Руди Джекобс 
(Matthew Rudy Jacobs), Джо Осборн (Joe Osborne), Дейв Страусс 
(Dave Strauss), Дерек Ло (Derek Law), Фредерик Чанг (Frederick 
Cheung), Хьюго Тайсон (Hugo Tyson), Пол Грегори (Paul Gregory), 
Стефен Спенсер (Stephen Spencer), Алекс Никсон (Alex Nixon), Бен 
Коппин (Ben Coppin), Кит Смитерс (Kit Smithers), Эндрю Икотт 
(Andrew Eacott), Фриланд Эбботт (Freeland Abbott), Джеймс Алей 
(James Aley), Мэтью Уилсон (Matthew Wilson), Симон Добсон (Simon 
Dobson), Дуг Орр (Doug Orr), Джонас Бонер (Jonas Bonér), Стью 
Хэллоуэй (Stu Halloway), Рич Морин (Rich Morin), Девид Уайттакер 
(David Whittaker), Бо Ридберг (Bo Rydberg), Джейк Гулдинг (Jake 
Goulding), Арии Голд (Ari Gold), Хуан Мануэль Химено Илла 
(Juan Manuel Gimeno Illa), Стив Бассетт (Steve Bassett), Норберто 
Ортигоза (Norberto Ortigoza), Лучано Рамальо (Luciano Ramalho), 
Шива Джайараман (Siva Jayaraman), Шон Перри (Shaun Parry) и 
Джоэл Ван дер Верф (Joel Van der Werf).
Особое спасибо научным рецензентам книги (и снова без какого-то 
особого порядка): Карлос Сесса (Carlos Sessa), Денни Вудс (Danny 
Woods), Венкат Субраманиам (Venkat Subramaniam), Симон Вуд 
(Simon Wood), Пайди Крид (Páidí Creed), Ян Ругли (Ian Roughley), 
Эндрю Томсон (Andrew Thomson), Эндрю Хейли (Andrew Haley), 
Шон Эллис (Sean Ellis), Джеффри Клементс (Geoffrey Clements), 
Лорен Сандс-Рамшау (Loren Sands-Ramshaw) и Пол Хадсон (Paul 
Hudson).
Наконец, я хочу принести слова благодарности и свои извинения 
моим друзьям, коллегам и родным. Спасибо за вашу поддержку и извините, 
что стремился к уединению последние восемь месяцев.

Благодарности
Доступ онлайн
559 ₽
В корзину