Создавая задачу A из задачи B, я думал, что задача A унаследует задачу B. Но при запуске следующего кода, который я создал, почему строка 14 была напечатана debug 4: <NSThread: 0x30361a300>{number = 6, name = (null)}
. Почему это не основная нить, как и другие гравюры?
Вот код для тех, кто хочет запустить локально:
struct Test {
func test() {
print("debug 3:", Thread.current)
Task {
print("debug 4:", Thread.current)
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Task { @MainActor in
print("debug 1:", Thread.current)
Task {
print("debug 2:", Thread.current)
Test().test()
}
}
}
}
Функция test
не изолирована от актера. Таким образом, его Task {…}
также не изолирован от актера. (Если бы test
был изолирован от актера, то его Task {…}
была бы задачей верхнего уровня для того же актера. Но test
не был изолирован от актера, так что это спорный вопрос.)
В параллелизме Swift изоляция функции либо определяется явно, либо наследуется от типа, в котором она определена, если таковой имеется, но она не «наследует» ее от вызывающей стороны.
Теоретически вы могли бы просто добавить к определению функции глобальный квалификатор актера, например, @MainActor func test() {…}
. Но немного странно иметь изолированную функцию в struct
, потому что это тип значения (с помощью которого мы обычно достигаем потокобезопасности, просто предоставляя каждому потоку свою собственную копию). Вероятно, нам нужно увидеть практический пример того, что вы пытаетесь сделать в test
, чтобы дать вам дальнейшие советы. Я также мог бы отослать вас к stackoverflow.com/a/76868102/1271826, который, рассматривая другой вопрос, иллюстрирует как безопасное, так и небезопасное взаимодействие с struct
.
Спасибо @Rob, да, это имеет смысл. Я также опубликовал ответ, основанный на вашем. Дайте мне знать, если что-то не так, спасибо!
Я привел несколько дополнительных примеров для объяснения ответа @Rob.
Пример 1:
test()
— это nonisolated
, он будет использовать фоновый поток, поэтому debug 3
печатает поток bg. Задача, созданная в строке 13, является задачей верхнего уровня и не привязана ни к какому актеру, поэтому в этом случае она использовала случайный поток bg.Пример 2:
UIViewController
использует MainActor, поэтому и debug 3
, и debug 4
выполняются в основном потоке, поскольку test()
— это функция класса MainActor.Пример 3:
debug 3
и debug 4
будут использовать поток bg, потому что test()
— это функция nonisolated
, даже если она находится в классе MainActor.Вместо «случайного фонового потока» можно отметить поток из «совместного пула потоков» (которого, кстати, следует избегать блокировки, поскольку этот пул ограничен количеством ядер вашего устройства).
Обратите внимание, что в Task {…}
в viewDidLoad
@MainActor in
является избыточным. UViewController
изолирован от главного актера, как и его методы в вашем подклассе, поэтому Task
уже изолирован от главного актера.
Спасибо за заметки! Да, @MainActor является избыточным, я добавил его только для того, чтобы было ясно, что это в MainActor.
Спасибо @Роб! Как бы я отметил эту функцию как изолированного актера?