Этим видео мы начинаем серию лекций про проблемы многопоточности. Как мы уже упоминали в предыдущих лекциях, одной из основных сложностей при создании многопоточного приложения является реализация потока безопасности. Код называется потокобезопасным, если он функционирует исправно при использовании его из нескольких потоков одновременно. В частности он должен обеспечивать правильный доступ нескольких потоков к разделяемым данным. К сожалению, при проектировании многопоточного приложения допускаются ошибки, что приводит к нарушению потока безопасности и возникновению проблем многопоточности, таких как состояние гонки, Deadlock, Livelock, инверсия приоритетов, о которых мы будем говорить далее. Race condition или состояние гонки, называется ошибка, проектирования предложения с использованием многопоточности, при которой нарушается работа логики приложения вследствие случайного изменения очередности выполнения задач на разных потоках. На первом слайде мы видим с вами очередь задач выполняющихся на одном потоке. В случае одного потока все задачи обслуживаются по принципу FIFO, First in First out, первый вошел, первый вышел. В данном подходе все задачи будут выполняться в той последовательности, в которой мы их разместили на поток, и результат выполнения этих задач будет предсказуемым. На следующем слайде мы видим, что каждая задача была размещена на отдельном потоке. В данном подходе мы можем быть уверены только в том, в какой момент времени мы поставили нашу задачу на выполнение, в какой поток и всё. В случае многопоточности, мы не можем предугадать какая задача на каком потоке будет выполнена раньше. Если порядок выполнения задач нарушается и это приводит к нарушению работы логики приложения, то такая ситуация и называется состоянием гонки. Одной из проблем, связанных с race condition, является возможность одновременного доступа к общим ресурсам, областям памяти или файлам. Часть кода, которая не должна выполняться на нескольких потоках одновременно, так как может привести к состоянию гонки, называется критической областью или критической секцией. Давайте представим такую ситуацию, что в нашем приложении выполняется две функции, каждая из которых получает доступ к одному и тому же значению в ячейке памяти. Первая функция вначале выполняет вычисления и записывает новое значение в ячейку памяти, а вторая функция считывает новое значение из ячейки памяти и отображает его в пользовательском интерфейсе. В случае выполнения этих задач на разных потоках состояние гонки может произойти ситуация, когда задача 2 выполнится первой, в результате чего произойдёт чтение из области и отображение в пользовательском интерфейсе устаревших данных, и как следствие, неправильная работа всего приложения. Давайте рассмотрим ещё один пример одновременного доступа к общим ресурсам. На двух следующих слайдах показан пример идеального выполнения двух задач на разных потоках и в ситуации состояния гонки. На первом слайде мы видим идеальный вариант выполнения задач. Каждая задача получает доступ к критической области только после освобождения этой области предыдущей задачей. В результате логика приложения остаётся не нарушенной. На втором слайде мы видим состояние гонки: две задачи на двух потоках осуществили доступ к критической области одновременно. Чтение значений из области памяти произошло одновременно до момента записи нового значения одной из задач, что привело к нарушению логики приложения. Для избежания состояния гонки существует правило взаимного исключения. Если в приложении один процесс получает доступ к общему ресурсу, значению в памяти или файлу, то другие процессы должны быть исключены из доступа к этому же ресурсу до его освобождения. В следующих лекциях мы расскажем подробнее как избежать состояния гонки.