Дезинфицирующее средство для нитей в xcode выдает ошибку - PullRequest
0 голосов
/ 15 апреля 2020
func doSomething() -> Int {
    var sum = 0
    let increaseWork = DispatchWorkItem {
        sum = sum + 100 //point 1
    }
    DispatchQueue.global().async(execute:increaseWork)
    increaseWork.wait()
    return sum //point 2
}

Thread Sanitizer говорит, что между точкой 1 и точкой 2 существует условие гонки, но я не думаю, что существует какое-либо условие гонки, так как метод changeWork.wait () блокирует вызов и не будет проходить до закрытия выполняется.

1 Ответ

0 голосов
/ 15 апреля 2020

Между этими двумя точками может быть состояние гонки. Представьте себе, что произойдет, если вы выполните функцию doSomething() в двух отдельных потоках, например:

  1. Первый поток выполняет закрытие increaseWork() и завершает его. Прямо сейчас он находится на линии ожидания
  2. Второй поток начинает выполнение и нажимает асин c инструкция выполнения
  3. Первый поток обращается к инструкции возврата, поскольку ожидание может продолжаться
  4. В то же время запланировано и выполняется замыкание со второй стороны

На данный момент вы не можете точно сказать, что выполняется первым: sum = sum + 100 из второго потока или return sum от первого.

Идея состоит в том, что sum является общим ресурсом, который не синхронизирован, поэтому, теоретически, это может быть условием гонки. Даже если вы позаботились о том, чтобы этого не произошло, Thread Sanitizer обнаруживает возможное состояние гонки, поскольку он не знает, запускаете ли вы один поток или выполняете функцию doSomething() из 100 различных потоков одновременно.

ОБНОВЛЕНИЕ:

Поскольку я пропустил тот факт, что переменная sum является локальной, приведенное выше объяснение не отвечает на текущий вопрос. Описанный сценарий никогда не будет реализован в данном фрагменте кода.

Но, хотя sum является локальной переменной, из-за того, что она используется и сохраняется внутри замыкания, она будет выделена в куче, а не в стеке. Это связано с тем, что компилятор Swift не может определить, будет ли закрытие завершено до выполнения функции doSomething() или нет.

Почему? Потому что замыкание передается конструктору, который ведет себя как параметр @escaping. Это означает, что нет гарантии того, что замыкание будет выполнено, поэтому все переменные, сохраняемые замыканием, должны быть размещены в куче для безопасности. Не зная, когда будет выполнено замыкание, Thread Sanitizer не может определить, будет ли действительно выполняться оператор return sum после того, как замыкание завершится.

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

...