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
Типы значений: данные будут использоваться в коде для нескольких потоков.
Данные не будут меняться по потокам после назначения. Не беспокойтесь о других потоках, так как каждый поток будет иметь свою собственную копию.
Типы ссылок: вы хотите создать совместное, изменяемое состояние
Данные могут изменяться любым из потоков, которые будут влиять на другие потоки, а также с его ссылкой на тип (т.е. указывая на одни и те же данные в памяти).