Условия гонки и взаимоблокировки

Visual Basic .NET или Visual Basic впервые предлагает возможность использовать потоки в приложениях Visual Basic. Потоки представляют проблемы отладки, такие как условия гонки и взаимоблокировки. В этой статье рассматриваются эти две проблемы.

Исходная версия продукта: Visual Basic, Visual Basic .NET
Исходный номер базы знаний: 317723

При возникновении условий гонки

Условие гонки возникает, когда два потока обращаются к общей переменной одновременно. Первый поток считывает переменную, а второй поток считывает то же значение из переменной. Затем первый поток и второй поток выполняют свои операции со значением, и они гонки, чтобы увидеть, какой поток может записать значение последним в общую переменную. Значение потока, записывавшего последнее значение, сохраняется, так как поток записывает значение по сравнению со значением, записанным предыдущим потоком.

Сведения и примеры состояния гонки

Каждому потоку выделяется предопределенный период времени для выполнения на процессоре. По истечении времени, выделенного для потока, контекст потока сохраняется до следующего включения процессора, и обработчик начинает выполнение следующего потока.

Как однострочный вызов команды может вызвать состояние гонки

Изучите следующий пример, чтобы увидеть, как происходит состояние гонки. Существует два потока, и оба обновляют общую переменную total (которая представлена как dword ptr ds:[031B49DCh] в коде сборки).

  • Поток 1

    Total = Total + val1
    
  • Поток 2

    Total = Total - val2
    

Код сборки (с номерами строк) из компиляции предыдущего кода Visual Basic:

  • Поток 1

    1. mov eax,dword ptr ds:[031B49DCh]
    2. add eax,edi
    3. jno 00000033
    4. xor ecx,ecx
    5. call 7611097F
    6. mov dword ptr ds:[031B49DCh],eax
    
  • Поток 2

    1. mov eax,dword ptr ds:[031B49DCh]
    2. sub eax,edi
    3. jno 00000033
    4. xor ecx,ecx
    5. call 76110BE7
    6. mov dword ptr ds:[031B49DCh],eax
    

Просмотрев код сборки, можно увидеть, сколько операций процессор выполняет на нижнем уровне для выполнения простого вычисления сложения. Поток может выполнять весь код сборки или его часть во время работы на процессоре. Теперь посмотрите, как происходит состояние гонки из этого кода.

Total — 100, val1 — 50, — val2 15. Поток 1 получает возможность выполнить, но выполняет только шаги 1–3. Это означает, что поток 1 считывает переменную и завершает добавление. Поток 1 теперь просто ожидает, чтобы записать новое значение 150. После остановки потока 1поток 2 выполняется полностью. Это означает, что она записала вычисляемое значение (85) в переменную Total. Наконец, поток 1 восстанавливает контроль и завершает выполнение. Он записывает значение (150). Таким образом, после завершения потока 1 значение Total теперь равно 150, а не 85.

Вы можете увидеть, как это может быть серьезной проблемой. Если это банковская программа, клиент будет иметь деньги на своем счете, которые не должны присутствовать.

Эта ошибка является случайной, так как поток 1 может завершить выполнение до истечения времени на процессоре, а затем поток 2 может начать выполнение. Если происходят эти события, проблема не возникает. Выполнение потока недетерминировано, поэтому нельзя управлять временем или порядком выполнения. Также обратите внимание, что потоки могут выполняться по-разному в режиме выполнения и в режиме отладки. Кроме того, можно увидеть, что при последовательном выполнении каждого потока ошибка не возникает. Такая случайность значительно усложняет отслеживание и отладку этих ошибок.

Чтобы предотвратить возникновение условий гонки, можно заблокировать общие переменные, чтобы доступ к общей переменной был только у одного потока за раз. Делайте это экономно, так как если переменная заблокирована в потоке 1 и потоку 2 также требуется переменная, выполнение потока 2 останавливается, а поток 2 ожидает освобождения переменной потоком 1 . (Дополнительные сведения см SyncLock . в разделе Ссылки этой статьи.)

Симптомы для состояния гонки

Наиболее распространенным симптомом состояния гонки являются непредсказуемые значения переменных, которые совместно используются несколькими потоками. Это происходит из-за непредсказуемости порядка, в котором выполняются потоки. Иногда побеждает один поток, а иногда выигрывает другой поток. В других случаях выполнение работает правильно. Кроме того, если каждый поток выполняется отдельно, значение переменной будет работать правильно.

При возникновении взаимоблокировок

Взаимоблокировка возникает, когда два потока блокируют другую переменную одновременно, а затем пытаются заблокировать переменную, которая уже заблокирована другим потоком. В результате каждый поток перестает выполняться и ожидает освобождения переменной другим потоком. Так как каждый поток содержит переменную, которую требует другой поток, ничего не происходит, и потоки остаются взаимоблокировками.

Сведения и примеры взаимоблокировок

Следующий код содержит два объекта и LeftValRightVal:

  • Поток 1

    SyncLock LeftVal
        SyncLock RightVal
            'Perform operations on LeftVal and RightVal that require read and write.
        End SyncLock
    End SyncLock
    
  • Поток 2

    SyncLock RightVal
        SyncLock LeftVal
            'Perform operations on RightVal and LeftVal that require read and write.
        End SyncLock
    End SyncLock
    

Взаимоблокировка возникает, если потоку 1 разрешено блокировать LeftVal. Процессор останавливает выполнение потока 1 и начинает выполнение потока 2. Поток 2 блокирует, RightVal а затем пытается заблокировать LeftVal. Так как LeftVal блокирован, поток 2 останавливается и ожидает LeftVal освобождения. Так как поток 2 остановлен, потоку 1 разрешено продолжать выполнение. Поток 1 пытается заблокировать RightVal , но не может, так как поток 2 заблокировал его. В результате поток 1 начинает ждать, пока RightVal не станет доступным. Каждый поток ожидает другого потока, так как каждый поток заблокировал переменную, которую ожидает другой поток, и ни один поток не разблокирует переменную, которую он удерживает.

Взаимоблокировка возникает не всегда. Если поток 1 выполняет обе блокировки до остановки процессора, поток 1 может выполнить свои операции, а затем разблокировать общую переменную. После того как поток 1 разблокирует переменную, Поток 2 может продолжить ее выполнение, как и ожидалось.

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

Симптомы взаимоблокировок

Распространенный симптом взаимоблокировки заключается в том, что программа или группа потоков перестает отвечать на запросы. Это также называется зависанием. По крайней мере два потока ожидают переменную, которая заблокирована другим потоком. Потоки не продолжаются, так как ни один из потоков не освобождает свою переменную, пока не получит другую переменную. Вся программа может зависнуть, если программа ожидает завершения выполнения в одном или обоих этих потоках.

Что такое поток

Процессы используются для разделения различных приложений, выполняемых в указанное время на одном компьютере. Операционная система не выполняет процессы, а потоки. Поток — это единица выполнения. Операционная система выделяет потоку время процессора для выполнения задач потока. Один процесс может содержать несколько потоков выполнения. Каждый поток поддерживает собственные обработчики исключений, приоритеты планирования и набор структур, которые операционная система использует для сохранения контекста потока, если поток не может завершить выполнение во время его назначения процессору. Контекст хранится до следующего момента, когда поток получит время процессора. Контекст включает в себя все сведения, необходимые потоку для непрерывного выполнения. Эта информация включает набор регистров процессора потока и стек вызовов в адресном пространстве хост-процесса.

Ссылки

Дополнительные сведения см. в справке Visual Studio по следующим ключевым словам:

  • SyncLock. Разрешает блокировку объекта. Если другой поток пытается заблокировать этот же объект, он блокируется до выпуска первого потока. Используйте SyncLock тщательно, так как проблемы могут возникнуть в результате неправильного использования SyncLock. Например, эта команда может предотвратить условия гонки, но вызвать взаимоблокировку.

  • InterLocked. Позволяет выбрать набор потоковобезопасных операций с базовыми числовыми переменными.

Дополнительные сведения см. в разделе Потоки и потоки.