iOS 에서 다 중 네트워크 가 요청 한 스 레 드 보안 에 대한 자세 한 설명
7410 단어 ios다 중 네트워크 요청스 레 드 보안
iOS 네트워크 프로 그래 밍 에서 흔히 볼 수 있 는 장면 은 두 가지 요 구 를 병행 처리 하고 모두 성공 한 후에 야 다음 처 리 를 할 수 있다 는 것 이다.다음은 일부 흔히 볼 수 있 는 처리 방식 이지 만 사용 과정 에서 도 오류 가 발생 하기 쉽다.
DispatchGroup.wait()
과DispatchGroup.notify()
을 통 해 성공 적 으로 처리 합 니 다.동시 프로 그래 밍 의 도전
동시 다발 적 인 사고 문 제 를 사용 하 는 것 은 매우 어렵다.대부분 우 리 는 이 야 기 를 읽 는 방식 으로 코드 를 읽는다.첫 줄 에서 마지막 줄 까지.코드 의 논리 가 선형 이 아니라면 우리 에 게 어느 정도 이해 의 어려움 을 줄 수 있다.단일 스 레 드 환경 에서 여러 가지 유형 과 프레임 워 크 를 디 버 깅 하고 추적 하 는 프로그램 이 실 행 된 것 은 이미 매우 골 치 아 픈 일이 다.다 중 스 레 드 환경 에서 이런 상황 은 상상 할 수 없다.
데이터 경쟁 문제:다 중 스 레 드 병행 환경 에서 데이터 읽 기 작업 은 스 레 드 가 안전 하고 쓰기 작업 은 비 스 레 드 가 안전 합 니 다.여러 스 레 드 가 동시에 메모리 에 쓰기 작업 을 하면 데이터 경쟁 으로 인해 잠재 적 인 데이터 오류 가 발생 할 수 있 습 니 다.
다 중 스 레 드 환경 에서 의 동태 행 위 를 이해 하 는 것 자체 가 쉬 운 일이 아니 므 로 데이터 경쟁 을 야기 하 는 스 레 드 를 찾 는 것 이 더욱 번거롭다.비록 우 리 는 상호 배척 잠 금 체 제 를 통 해 데이터 경쟁 문 제 를 해결 할 수 있 지만 수정 가능 한 코드 에 있어 서 상호 배척 잠 금 체제 의 유 지 는 매우 어 려 운 일이 다.
테스트 하기 어렵다:병발 환경 에서 많은 문제 가 개발 과정 에서 나타 나 지 않 는 다.Xcode 와 LLVM 은 이러한 문 제 를 검사 하 는 데 사용 되 는Thread Sanitizer도 구 를 제공 하지만 이러한 문제 들 의 디 버 깅 과 추적 은 여전히 매우 어렵다.병발 환경 에서 코드 자체 의 영향 을 제외 하고 응용 도 시스템 의 영향 을 받 기 때문이다.
병발 상황 을 처리 하 는 간단 한 방법
병행 프로 그래 밍 의 복잡성 을 고려 하여 우 리 는 병행 하 는 여러 가지 요 구 를 어떻게 해결 해 야 합 니까?
가장 쉬 운 방법 은 병렬 코드 를 만 드 는 것 을 피 하 는 것 이 아니 라 여러 개의 요청 이 선형 으로 연결 되 어 있 는 것 입 니 다.
let session = URLSession.shared
session.dataTask(with: request1) { data, response, error in
// check for errors
// parse the response data
session.dataTask(with: request2) { data, response error in
// check for errors
// parse the response data
// if everything succeeded...
callbackQueue.async {
completionHandler(result1, result2)
}
}.resume()
}.resume()
코드 의 간결 함 을 유지 하기 위해 서 여 기 는 많은 세부 처 리 를 무시 합 니 다.예 를 들 어 오류 처리 와 취소 요청 등 입 니 다.그러나 이렇게 하면 관련 이 없 는 요청 선형 정렬 에 문제 가 숨 어 있다.예 를 들 어 서버 가 HTTP/2 프로 토 콜 을 지원 한다 면 우 리 는 HTTP/2 프로 토 콜 에서 같은 링크 를 통 해 여러 요청 을 처리 하 는 특성 을 보 내지 않 았 고 선형 처리 도 프로세서 의 성능 을 잘 이용 하지 못 했다 는 것 을 의미한다.URLSession 에 대한 잘못된 인식
가능 한 데이터 경쟁 과 스 레 드 안전 문 제 를 피하 기 위해 서 나 는 위의 코드 를 내장 요청 으로 바 꾸 었 다.즉,동시 요청 으로 바 꾸 면 요청 은 끼 워 넣 을 수 없 으 며,두 요청 은 같은 메모리 에 대해 쓰기 작업 을 할 수 있 으 며,데이터 경쟁 은 재현 과 디 버 깅 이 매우 어렵다.
문 제 를 해결 하 는 실행 가능 한 방법 은 잠 금 체 제 를 통 해 한 동안 하나의 스 레 드 만 공유 메모리 에 대해 쓰기 작업 을 할 수 있 도록 하 는 것 이다.잠 금 메커니즘 의 실행 과정 도 매우 간단 하 다.잠 금 요청,실행 코드,잠 금 해제.물론 자물쇠 메커니즘 을 완전히 정확하게 사용 하려 면 약간의 기교가 있다.
그러나 URLSession 의문서.설명 에 따 르 면 여기 에는 동시 다발 요청 의 더욱 간단 한 해결 방안 이 있다.
init(configuration: URLSessionConfiguration,
delegate: URLSessionDelegate?,
delegateQueue queue: OperationQueue?)
[…]queue : An operation queue for scheduling the delegate calls and completion handlers. The queue should be a serial queue, in order to ensure the correct ordering of callbacks. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
이것 은 모든 URLSession 의 인 스 턴 스 대상 이 URLSession.shared 단일 사례 의 리 셋 을 포함 하여 동시에 실행 되 지 않 는 다 는 것 을 의미 합 니 다.
URLSession 확장 및 지원
위 에서 URLSession 에 대한 새로운 인식 을 바탕 으로 스 레 드 의 안전 한 병행 요청(코드 주소 완성)을 지원 하도록 확대 합 니 다.
enum URLResult {
case response(Data, URLResponse)
case error(Error, Data?, URLResponse?)
}
extension URLSession {
@discardableResult
func get(_ url: URL, completionHandler: @escaping (URLResult) -> Void) -> URLSessionDataTask
}
// Example
let zen = URL(string: "https://api.github.com/zen")!
session.get(zen) { result in
// process the result
}
우선,우 리 는 URLSession DataTask 리 셋 에서 얻 을 수 있 는 다양한 결 과 를 모 의 하기 위해 간단 한 URL Result 매 거 진 을 사용 했다.이 매 거 진 유형 은 여러 개의 동시 요청 결 과 를 간소화 하 는 데 유리 하 다.여기 서 글 의 간결 함 을 위해URLSession.get(_:completionHandler:)
방법 이 완전 하 게 구현 되 지 않 았 습 니 다.이 방법 은 GET 방법 으로 해당 하 는 URL 을 요청 하고 자동 으로 실행resume()
한 다음 에 실행 결 과 를 URL Result 대상 으로 밀봉 하 는 것 입 니 다.
@discardableResult
func get(_ left: URL, _ right: URL, completionHandler: @escaping (URLResult, URLResult) -> Void) -> (URLSessionDataTask, URLSessionDataTask) {
}
이 세그먼트 API 코드 는 두 개의 URL 인 자 를 받 아들 이 고 두 개의 URLSession DataTask 인 스 턴 스 를 되 돌려 줍 니 다.다음 코드 는 함수 실현 의 첫 번 째 부분 입 니 다.
precondition(delegateQueue.maxConcurrentOperationCount == 1,
"URLSession's delegateQueue must be configured with a maxConcurrentOperationCount of 1.")
URLSession 대상 을 예화 할 때 도 동시 다발 적 인 Operation Queue 대상 에 들 어 갈 수 있 기 때문에 이 코드 를 사용 하여 이러한 상황 을 제거 해 야 합 니 다.
var results: (left: URLResult?, right: URLResult?) = (nil, nil)
func continuation() {
guard case let (left?, right?) = results else { return }
completionHandler(left, right)
}
이 코드 를 실현 에 계속 추가 합 니 다.그 중에서 결 과 를 되 돌려 주 는 메타 변수 results 를 정의 합 니 다.또한,우 리 는 함수 내부 에서 두 요청 이 모두 결과 처 리 를 마 쳤 는 지 확인 하기 위해 다른 도구 함 수 를 정의 했다.
let left = get(left) { result in
results.left = result
continuation()
}
let right = get(right) { result in
results.right = result
continuation()
}
return (left, right)
마지막 으로 이 코드 를 실현 에 추 가 했 습 니 다.그 중에서 우 리 는 각각 두 개의 URL 을 요 청 했 고 요청 이 모두 끝 난 후에 결 과 를 되 돌려 주 었 습 니 다.주의해 야 할 것 은 여기 서 우 리 는 두 번 의 집행continuation()
을 통 해 요청 이 모두 완성 되 었 는 지 판단 하 는 것 이다.continuation()
할 때 한 요청 이 완료 되 지 않 았 기 때문에 리 셋 함 수 는 실행 되 지 않 습 니 다.
extension URLResult {
var string: String? {
guard case let .response(data, _) = self,
let string = String(data: data, encoding: .utf8)
else { return nil }
return string
}
}
URLSession.shared.get(zen, zen) { left, right in
guard case let (quote1?, quote2?) = (left.string, right.string)
else { return }
print(quote1, quote2, separator: "
")
// Approachable is better than simple.
// Practicality beats purity.
}
병렬 역설나 는 병렬 문 제 를 해결 하 는 가장 간단 하고 우아 한 방법 은 가능 한 한 병렬 프로 그래 밍 을 적 게 사용 하 는 것 이 며,우리 의 프로 세 서 는 선형 코드 를 실행 하기에 매우 적합 하 다 는 것 을 발견 했다.그러나 큰 코드 블록 이나 작업 을 여러 개의 병렬 실행 코드 블록 과 작업 으로 나 누 면 코드 를 더욱 쉽게 읽 고 유지 할 수 있 습 니 다.
총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
저자:아담 샤프,시간:2017/9/21
오류 가 있 으 면 지적 을 환영 합 니 다.링크
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Swift의 패스트 패스Objective-C를 대체하기 위해 만들어졌지만 Xcode는 Objective-C 런타임 라이브러리를 사용하기 때문에 Swift와 함께 C, C++ 및 Objective-C를 컴파일할 수 있습니다. Xcode는 S...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.