우선순위 역전, 시퀀스 프로토콜

rbw·2023년 3월 3일
0

TIL

목록 보기
74/99

우선순위 역전

참조

https://sujinnaljin.medium.com/ios-%EC%B0%A8%EA%B7%BC%EC%B0%A8%EA%B7%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-gcd-15-3fef697f9aab

위 글을 보고 간단히 정리한 글, 자세한 내용은 위 블로그 참조 정리퀸이심;


동시성 프로그래밍에서 일어나는 문제 중에 하나인 우선순위의 역전에 대해서 간단히 살펴보겠습니다.

우선순위가 .default, .userInitiated, userInteractive 이렇게 있다고 할 때, 먼저, default 우선순위를 가진 task를 먼저 작업을 실행한다고 가정합니다. 이를 task1로 부르겠슴니다

  • task1은 thread4에서 작업이 실행되고 공유 자원인 a를 선점합니다
  • task2 (.userInitiated 우선순위)인 작업이 실행이 된다고 하면, task1은 멈추게됩니다. 우선순위가 낮기 떄문이져
  • task3인 작업이 들어오면, task2도 멈추고, task3가 실행이 됩니다.

이런 상황이 있다고 할 때, 문제가 발생하는 경우는 task3가 공유 자원인 a를 사용하는 경우입니다. 이미 a는 task1(default 우선순위)이 점유하고 있기 때문에, task3는 우선순위가 높음에도 불구하고 실행이 멈추게 됩니다.

이 때는 작업의 완료 순서가 (task 2 -> task 1 -> task 3) 순으로 완료됩니다. (a를 점유한 task1이 먼저 종료되는 모습 ~)

이를 우선순위의 역전이라고 합니다. 우선순위가 높은 순대로 종료가 안 되고 있죠

swift는 이를 방지하기 위해 GCD를 사용해 우선순위를 조정하는 모습을 볼 수 있습니다.

위의 경우로 생각을 할 때, default인 task1이 공유 자원을 점유하고 있으니 이 task1을 빨리 끝내야 한다고 판단을하여 우선순위를 올려버립니다. 따라서 task1은 .userInteractive로 우선순위가 조정이 되어서 제일 먼저 작업이 완료가 됩니다.

그런 다음 우선순위가 높은 작업인 task3를 실행하고 완료됩니다. 나머지 task2 도 다음으로 완료됩니다.

이 때의 작업 완료 순서는(task1 -> task3 -> task2) 입니다.

이렇게 GCD에서 자체적으로 처리하는 것 외에도, 공유자원을 접근할 때는 동일한 우선순위를 사용해야 우선순위 역전 문제의 가능성을 줄일 수 있슴니다.

프로토콜의 요구사항, 시퀀스 프로토콜

참조

https://github.com/apple/swift/blob/main/docs/SequencesAndCollections.rst#index-protocols

위 문서 읽으면서 괜찮았던 부분을 작성해보려고 함. 자세한 내용은 위 문서 참조 바람


스위프트 깃헙에서 for...in 에서 무슨 작업이 필요할지 정의를 내리고 프로토콜을 작성하는 부분이 있는데 이거 좀 생각하면서 개발하면 좋을듯해서 적어봄

먼저 for...in 반복자의 작업이 뭐가 필요할지 생각해봄 -> 세 가지 작업이 필요함

  • 현재 요소를 가져온다.
  • 다음 요소로 진행
  • 더 많은 요소가 있는지 감지

위 내용을 프로토콜 요구사항으로 그대로 번역하면 다음과 같다

protocol NaiveIteratorProtocol {
    typealias Element
    var current() -> Element // 현재 요소 가져오기
    mutating func advance() // 다음 요소로 진행
    var isExhausted: Bool // 더 많은 요소가 있는지 감지
}

그러나 이러한 프로콜은 휘발성 시퀀스(네트워크 패킷 스트림 같은거 순회하면서 소비하는 친구들)에게는 부담을 줍니다.

반복자는 현재 요소를 내부적으로 버퍼링하여 current가 동일한 값을 반복적으로 반환할 수 있도록 하거나 moveToNext에 대한 중간 호출 없이 current가 두 번 호출될때 트랩(chatGPT 선생님 왈 프로그램이 처리할 수 없는 오류나 예외적인 조건으로 인해 프로그램이 의도적으로 강제로 종료되거나 충돌 되는 상황, 두 번 호출되면 종료라는 의미로 받아들이면 될 거 가틈)해야함.

따라서 Swift의 반복자프로토콜은 세 가지 작업을 하나로 병합하여 반복자가 소진되면 nil을 반환한다.

protocol IteratorProtocol {
    typealias Element // associatedtype 으로 쓰면 될듯 이젠 ?
    mutating func next() -> Element?
}

추가로 정방향(Forward Index), 양방향, 임의 액세스 인덱스 프로토콜을 보게씀다.

순방향은 생략, 얘는 앞으로 전진 하는것과, 끝 지점인지 감지하는 함수가 있으면 된다.

// 양방향은 순방향에서 좀 더 추가된 것
protocol BidirectionalIndexType : ForwardIndexType {
  func predecessor() -> Self
}

// 랜덤액세스, 얘는 거리를 알아야함 그리고 음수를 포함하여 더해서 진행가능한지도 알아야한다.
public protocol RandomAccessIndexType : BidirectionalIndexType {
  func distance(to other: Self) -> Distance
  func advanced(by n: Distance) -> Self
}

임의 액세스 프로토콜은 양방향 프로토콜에서 두 가지 요구사항이 더해진것이다.

profile
hi there 👋

0개의 댓글