Apple описание ссылочных и типов значений с несколькими потоками

Я читаю от Apple документация. Я думал, что знаю, когда выбрать тип значения и когда выбрать тип ссылки, но я вернулся к Swif101. В документации указано:

  • Типы значений: Данные будут использоваться в коде для нескольких потоков.
  • Типы ссылок:. Вы хотите создать совместное, изменяемое состояние

Не используются ли ссылочные типы для нескольких потоков? Какая разница в этих двух строках?

Ответ 1

Как указывали другие, типы ссылок всегда передают указатель на объект, который идеально подходит для того, где вы хотите "разделяемое, изменяемое состояние" (как указано в упомянутом документе). Ясно, однако, если вы мутируете/получаете доступ к ссылочному типу по нескольким потокам, убедитесь, что вы синхронизировали свой доступ к нему (через выделенную последовательную очередь, шаблон считывателя, блокировки и т.д.).

Типы значений немного сложнее. Да, как указывали другие, если вы передаете тип значения в качестве параметра методу, который затем выполняет что-то в другом потоке, вы, по сути, работаете с копией этого типа значений (примечание Джоша о копировании в режиме on- пишите, несмотря на это). Это обеспечивает целостность объекта, переданного методу. Этот штраф (и был достаточно охвачен другими ответами здесь).

Но он становится более сложным, когда вы имеете дело с закрытием. Рассмотрим, например, следующее:

struct Person {
    var firstName: String
    var lastName: String
}

var person = Person(firstName: "Rob", lastName: "Ryan")

DispatchQueue.global().async {
    Thread.sleep(forTimeInterval: 1)
    print("1: \(person)")
}

person.firstName = "Rachel"
Thread.sleep(forTimeInterval: 2)
person.lastName = "Moore"
print("2: \(person)")

Очевидно, что вы обычно не были бы sleep, но я делаю это, чтобы проиллюстрировать суть: именно, хотя мы имеем дело с типом значения и несколькими потоками, person вы ссылаетесь на закрытие это тот же самый экземпляр, с которым вы работаете в основном потоке (или в любом потоке, на котором это выполнялось), а не на его копии. Если вы имеете дело с изменяемым объектом, это не безопасно для потоков.

Я проиллюстрировал этот пример, чтобы проиллюстрировать этот момент, в котором оператор print в закрытии выше будет сообщать "Rachel Ryan", эффективно отображая состояние типа значения person в несогласованном состоянии.

При закрытии с использованием типов значений, если вы хотите использовать семантику значения, вы должны изменить этот вызов async для использования отдельной переменной:

let separatePerson = person
queue.async {
    Thread.sleep(forTimeInterval: 1)
    print("1: \(separatePerson)")
}

Или, что еще проще, используйте "список захвата", который указывает, какие переменные типа значения должны быть захвачены закрытием:

queue.async { [person] in
    Thread.sleep(forTimeInterval: 1)
    print("1: \(person)")
}

В любом из этих примеров вы теперь наслаждаетесь семантикой значений, копируете объект, а оператор print корректно сообщает "Rob Ryan", даже если исходный объект person мутируется в другом потоке.

Итак, если вы имеете дело со типами значений и замыканиями, типы значений могут делиться по потокам, если вы явно не используете список захвата (или что-то подобное), чтобы использовать семантику значений (то есть копировать объект по мере необходимости).

Ответ 2

Да, ссылки совместно, если их используют несколько потоков; что именно проблема. Все потоки относятся к тем же фактическим данным в памяти. Затем им требуются механизмы синхронизации, чтобы гарантировать, что чтение и запись отдельных потоков не конфликтуют. Эти механизмы имеют затраты на сложность кода и производительность.

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


* При стандартном исключении copy-on-write для типов Swift stdlib: фактическая копия выполняется только в том случае, если данные мутированы.

Ответ 3

Это смутно сформулировано.

Типы значений: данные будут использоваться в коде для нескольких потоков.

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

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

Типы ссылок: вы хотите создать совместное, изменяемое состояние

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

Ответ 4

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

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

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

Ответ 5

Типы значений: данные будут использоваться в коде для нескольких потоков.

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

Типы ссылок: вы хотите создать совместное, изменяемое состояние

Данные могут изменяться любым из потоков, которые будут влиять на другие потоки, а также с его ссылкой на тип (т.е. указывая на одни и те же данные в памяти).