강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

개발자님의 프로필 이미지
개발자

작성한 질문수

앨런 Swift Concurrency for Swift 6 (Part-2)

Shared Mutable State (공유 가변 상태) 코드 설명 (10강)

10강 내용 문의드립니다.

해결된 질문

작성

·

44

·

수정됨

0

안녕하세요. 10강 내용 중 8분 40초 즈음에

Task 가 여러 개일 경우 '동일 시점에 여러개의 쓰레드에서 접근/사용 가능성이 있기 때문에' Race Condition이 발생할 수 있다고 설명해주셨는데요.

 

교재 10p에서 'Task는 현재 실행중인 컨텍스트의 메타데이터를 그대로 상속해서 사용'한다고 설명하고 있고, 메타데이터에는 '실행 액터'도 포함되어 있어서(현재 시점에서는 액터를 쓰레드와 비슷한 의미로 이해하고 있습니다.) , Task 클로저 외부와 내부의 액터(~= 쓰레드?)는 같을 것 같은데요. 이로 미루어 봤을 때, Task 1과 Task 2의 클로저가 실행되는 쓰레드도 같아야 할 것 같다는 생각이 들더라구요.

image.png

(swift 6모드에선 비동기 컨텍스트에서 Thread.current에 접근할 수 없어 swift 5 모드로 실행했습니다.)

간단히 테스트 해봤을 때 위처럼 두개의 Task의 클로저가 실행되는 쓰레드가 메인 쓰레드로 같은데요. 이는 test() 메서드가 MainActor에서 실행되기에(@MainActor) 이와 같은 쓰레드에서 실행되는 것으로 이해했습니다.

제가 잘못 이해하고 있는 지점이 있을 것 같은데요. 뒷 내용을 아직 듣지 못했지만 참지 못하고 질문드려봅니다! ㅎㅎ
답변해주시면 감사하겠습니다!

 

답변 2

1

앨런(Allen)님의 프로필 이미지
앨런(Allen)
지식공유자

네 안녕하세요!

지금 일단 질문주신 내용에서는, 왜 1번쓰레드라고 찍히는 지를 정확하게 이해하고 계십니다.

제가 말씀드린... Task 가 여러 개일 경우 '동일 시점에 여러개의 쓰레드에서 접근/사용 가능성이 있기 때문에' Race Condition이 발생할 수 있다고 설명드린 것은, Task로 비동기 처리를 하게 되는 일반적인 상황을 말씀드린 것이예요.

 

즉, 아래 코드와 같이 액터 내부의 메서드가 아닌, 일반 함수 또는 클래스, 구조체 등의 메서드로 구현하신 다면.. 예를 들어, 2번 쓰레드 / 3번 쓰레드로 동작하게 되니.. 각각의 Task 내부에서 데이터에 접근하게 되면 당연히 동시 접근이 일어날 수 있습니다.

func test() {
    Task {
         // 2번 쓰레드
    }

    Task {
         // 3번 쓰레드
    }
}

 

다만, 지금 윤종 님께서 만드신 코드의 경우, 스위프트UI 뷰 내부의 메서드로 만드셨기 때문에.. 지금 만드신 test( ) 메서드 자체가 @MainActor를 채택하게 됩니다. https://developer.apple.com/documentation/swiftui/view View프로토콜 공식문서에 들어가보시면 프로토콜 앞에 @MainActor 라고 붙어있고, 그 프로토콜을 채택하고 있기 때문에 ContentView 내부의 메서드들이 모두 @MainActor(1번 쓰레드)에서 실행되게 됩니다.
(쉽게 직관적으로 생각해보자면.. 스위프트UI에서 뷰 관련 코드이니, 1번 쓰레드에서 실행되는 것이예요.)

즉, 윤종 님께서 말씀하신 대로.. Task 는 액터내부에서 실행된다면, 해당 (실행)액터의 메타 데이터를 상속 받게 되기 때문에, 지금 현재 ContentView 안에서 test( ) 메서드를 구현하시면 결국 @MainActor가 붙어있는 메서드를 구현하신 것과 완전히 동일한 것이라고 보시면 됩니다.

@MainActor func test() {
    Task {
         // 1번 쓰레드 (메타 데이터 상속)
    }

    Task {
         // 1번 쓰레드 (메타 데이터 상속)
    }
}

 

그래서 당연히 결과도 1번 쓰레드라고 찍히고 있는 것이고요.

 

그래서 액터 내부에 메서드로 구현하시고 그 안에서 Task를 사용하시면, 그건 당연히 액터의 실행 컨텍스트를 물려 받아서(쉽게 말해 쓰레드를 물려 받기 때문에) 그렇게 하시면 안되고.. 액터 내부의 메서드가 아닌 일반적인 메서드나 함수로 구현하시면.. 당연히 일반적인 비동기 상황이기 때문에 여러쓰레드에서 동시에 접근하는 상황이 됩니다.
(물론 당연히 알고 계시겠지만, async/await을 사용하는 일반적인 비동기 상황은.. 대부분 액터 내부의 메서드가 아닐껍니다.)

 

아, 그리고 프로토콜 채택 규칙과 관련해서는.. 교재 p.285 내용강의 60강에서 설명드리고 있습니다. 참고하시면 될 것 같아요 !

 

 

 

궁금하신 내용이 해결되셨길 바라요!

 

 

감사합니다. :)

0

안녕하세요, 인프런 AI 인턴입니다. 윤종님께서 제기하신 Swift Concurrency에서의 Task와 스레드 관련 질문을 확인했습니다.

Race Condition과 Task의 실행

Swift의 Task는 비동기적으로 여러 스레드에서 실행될 수 있으며, 이는 Race Condition 발생 가능성을 내포합니다. Race Condition이란 여러 스레드가 동시에 동일한 자원에 접근/수정할 때 발생할 수 있는 문제로, 데이터의 일관성을 깨뜨릴 위험이 있습니다.

Task의 실행 스레드

Task는 현재 실행 중인 컨텍스트에서 메타데이터를 상속하지만, 이는 액터에서 실행된다는 보장을 의미하지는 않습니다. Task는 필요에 따라 다른 스레드에서 실행될 수 있으며, 실제로 여러 스레드에서 접근할 가능성이 존재합니다.

@MainActor와 스레드 실행

@MainActor
func test() {
    // Test code
}

위 코드처럼 @MainActor로 지정된 함수 내에서 실행된 Task는 기본적으로 메인 스레드에서 실행됩니다. 이는 UI 업데이트 등 메인 스레드에서 안전하게 실행해야 할 작업을 위해 설계되었습니다. 그러나 일반적인 상황에서는 Task는 어디에서든 자유롭게 실행될 수 있습니다.

추가 사항

  • Thread.current: Swift 6에서는 비동기 컨텍스트 내에서 Thread.current 사용에 제한이 있습니다. 이는 스레드 상태를 안전하게 관리하기 위함입니다.
  • MetaData와 Actor: ‘액터=스레드’라는 일반화는 정확하지 않습니다. 액터는 Swift의 동시성 모델에서 데이터를 보호하고 관리하기 위한 논리적 단위로, 스레드와는 개념적으로 다를 수 있습니다.

더 깊은 이해를 위해 Apple 공식 자료를 참고하시길 권장드립니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

개발자님의 프로필 이미지
개발자

작성한 질문수

질문하기