CancellationToken이 CancellationTokenSource와 다른 이유는 무엇입니까?


137

클래스 CancellationToken외에도 .NET 구조체가 도입 된 이유에 대한 근거를 찾고 CancellationTokenSource있습니다. API가 어떻게 사용되는지 이해 하지만 API가 그렇게 설계 되었는지 이해하고 싶습니다 .

즉, 왜 우리는 :

var cts = new CancellationTokenSource();
SomeCancellableOperation(cts.Token);

...
public void SomeCancellableOperation(CancellationToken token) {
    ...
    token.ThrowIfCancellationRequested();
    ...
}

다음 CancellationTokenSource과 같이 직접 전달하는 대신 :

var cts = new CancellationTokenSource();
SomeCancellableOperation(cts);

...
public void SomeCancellableOperation(CancellationTokenSource cts) {
    ...
    cts.ThrowIfCancellationRequested();
    ...
}

이는 토큰을 전달하는 것보다 취소 상태 확인이 더 자주 발생한다는 사실을 기반으로 성능 최적화입니까?

따라서 CancellationTokenSource추적 및 업데이트를 유지할 수 있으며 CancellationTokens각 토큰에 대해 취소 확인이 로컬 필드 액세스입니까?

두 가지 경우 모두 잠금이없는 휘발성 부울만으로 충분하다는 것을 감안할 때 여전히 더 빠른 이유를 알 수 없습니다.

감사!

답변:


110

나는이 클래스들의 디자인과 구현에 관여했다.

짧은 대답은 " 문제의 분리 "입니다. 다양한 구현 전략이 있으며 유형 시스템 및 초기 학습과 관련하여 일부가 더 간단하다는 것은 사실입니다. 그러나 CTS와 CT는 많은 시나리오 (딥 라이브러리 스택, 병렬 계산, 비동기 등)에서 사용하기위한 것으로 복잡한 사용 사례를 염두에두고 설계되었습니다. 성공적인 패턴을 장려하고 성능을 저하시키지 않으면 서 반 패턴을 방지하기위한 디자인입니다.

API가 잘못 작동하여 문이 열려 있으면 취소 설계의 유용성이 빠르게 사라질 수 있습니다.

CancellationTokenSource == "취소 트리거"및 연결된 리스너 생성

CancellationToken == "취소 청취자"


7
고마워요! 내부 지식은 정말 감사합니다.
Andrey Tarantsov

stackoverflow.com/questions/39077497/… StreamSocket의 입력 스트림에 대한 기본 시간 초과가 무엇인지 알고 있습니까? canceltoken을 사용하여 읽기 작업을 취소하면 해당 소켓도 닫힙니다. 이 문제를 극복 할 수있는 방법이 있습니까?
sam18

@Mike-궁금한 점이 있습니다 .CT 에서처럼 CTS에서 ThrowIfCancellationRequested ()와 같은 것을 어떻게 호출 할 수 없습니까?
rory.ap

그들은 다른 책임을 가지고 cts.Token.ThrowIfCancellationRequested ()를 작성하기에는 너무 장황하지 않으므로 CTS에 리스너 API를 직접 추가하지 않았습니다.
Mike Liddell

@Mike 왜 canceltoken이 클래스가 아닌 구조체입니까? 나는 성능에 이점이 없다
Neir0

85

나는 정확한 질문을 했고이 디자인의 근거를 이해하고 싶었습니다.

받아 들여진 대답은 근거가 옳았습니다. 이 기능을 디자인 한 팀의 확인 사항은 다음과 같습니다 (강조 광산).

두 가지 새로운 유형이 프레임 워크의 기초를 형성합니다. A CancellationToken는 '잠재 취소 요청'을 나타내는 구조체입니다. 이 구조체는 매개 변수로 메서드 호출에 전달되며 메서드는 해당 요청을 폴링하거나 취소 요청시 실행되도록 콜백을 등록 할 수 있습니다. A CancellationTokenSource는 취소 요청을 시작하는 메커니즘을 제공하는 클래스 Token 이며 관련 토큰을 얻기위한 속성이 있습니다. 이 두 클래스를 하나로 결합하는 것은 당연한 것이 었지만,이 설계를 통해 두 가지 주요 작업 (취소 요청 시작 및 취소 관찰 및 응답)을 완전히 분리 할 수 ​​있습니다. 특히, CancellationToken요청 만받는 메소드 는 취소 요청을 관찰 할 수 있지만이를 취소 할 수는 없습니다.

링크 : .NET 4 취소 프레임 워크

제 생각 CancellationToken에는 상태 만 관찰 할 수 있고 변경할 수 없다는 사실 은 매우 중요합니다. 당신은 사탕처럼 토큰을 나눠 줄 수 있고 당신 이외의 다른 누군가가 그것을 취소 할 것을 걱정하지 마십시오. 적대적인 제 3 자 코드로부터 사용자를 보호합니다. 예, 기회는 적지 만 개인적으로 그 보증을 좋아합니다.

또한 API를 더 깨끗하게 만들고 실수로 인한 실수를 피하며 더 나은 구성 요소 디자인을 촉진한다고 생각합니다.

이 두 클래스에 대한 퍼블릭 API를 살펴 보자.

CancellationToken API

CancellationTokenSource API

LongRunningFunction을 작성할 때 이들을 결합하면 사용하지 말아야 할 'Cancel'의 여러 과부하와 같은 메소드가 표시됩니다. 개인적으로 Dispose 메서드도 싫어합니다.

현재의 클래스 디자인은 '성공의 원칙'철학을 따르는 것으로 생각되며, 개발자가 Task취소 를 처리 할 수있는 더 나은 구성 요소를 만든 다음 여러 가지 방식으로 함께 구성하여 복잡한 워크 플로를 만들도록 안내합니다.

질문을하겠습니다. 토큰의 목적이 무엇인지 궁금하십니까? 나에게는 이해가되지 않았다. 그런 다음 Managed Threads에서 Cancel을 읽고 모든 것이 명확 해졌습니다.

TPL의 취소 프레임 워크 디자인은 절대 최고 수준이라고 생각합니다.


2
당신이 궁금해하는 경우 CancellationTokenSource가 (토큰 캔 자체로 그것을 할) 토큰 연관된에이 요청을 취소 시작 실제로 수 있습니다 : :이 CancellationToken이 내부 생성자가 internal CancellationToken(CancellationTokenSource source) { this.m_source = source; }이 속성을 : public bool IsCancellationRequested { get { return this.m_source != null && this.m_source.IsCancellationRequested; } }CancellationTokenSource 내부 생성자를 사용, 토큰이 참조를 가지고 있도록 출처 (m_source)
chviLadislav

65

기술적 인 이유가 아니라 의미적인 이유로 분리되어 있습니다. CancellationTokenILSpy 에서 구현을 살펴보면 래퍼 일뿐입니다 CancellationTokenSource(따라서 참조를 전달하는 것과 다른 성능 측면은 아닙니다).

그것들은 이러한 기능 분리를 제공하여 상황을보다 예측 가능하게 만듭니다 CancellationToken. 물론, 메소드는 여전히을 던질 수 TaskCancelledException있지만 CancellationToken자체 및 동일한 토큰을 참조하는 다른 메소드는 안전합니다.


감사! 내 깜박임 반응은 의미 적으로 IReadOnlyList를 제공하는 것과 비교할 때 의문의 여지가있는 접근법입니다. 그러나 매우 그럴듯하게 들리므로 답을 받아들이십시오.
Andrey Tarantsov

1
더 깊이 생각해 보면 나는 그럴듯함에 의문을 갖게된다. 그렇다면 CancellationTokenSource가 구현할 ICancellationToken 인터페이스를 제공하는 것이 더 합리적이지 않습니까? .NET 팀의 누군가가 참여하기를 바랍니다.
Andrey Tarantsov

그래도 여전히 숙련 된 프로그래머가에 캐스팅 될 수 CancellationTokenSource있습니다. 당신은 단지 "그렇게하지 말라"고 말할 수 있다고 생각하지만, 사람들 (나를 포함하여)은 어쨌든 숨겨진 기능을 얻기 위해 어쨌든 이런 일을합니다. 그것은 적어도 내 현재의 이론입니다.
코리 넬슨

1
보안과 관련이없는 한 대부분의 경우 API를 디자인하는 방식이 아닙니다. 차라리“향후 성능 최적화를 위해 옵션을 열어 두는 것”과 같은 다른 설명에 베팅하고 싶습니다. 그러나 여전히, 당신은 지금까지 최고입니다.
Andrey Tarantsov

안녕하세요, 구현에 관련된 사람에게 허용 된 답변을 다시 할당 해 주셔서 감사합니다. 둘 다 똑같은 말을하지만, 나는 결정적인 대답이 맨 위에 있다고 생각합니다.
Andrey Tarantsov

10

CancellationToken많은 사본 인해 방법에 따라 전달하기에 존재할 수 있도록 구조체이다.

CancellationTokenSource세트 호출 토큰의 모든 사본의 상태 Cancel소스에. 이 MSDN 페이지를 참조하십시오

디자인의 이유는 우려의 분리와 구조체의 속도의 문제 일 수 있습니다.


1
감사. 또 다른 대답은 기술적으로 (IL에서) CancellationTokenSource는 토큰의 상태를 설정하지 않는다는 것을 나타냅니다. 대신 토큰은 CancellationTokenSource에 대한 실제 참조를 래핑하고 단순히 액세스하여 취소를 확인합니다. 무엇이든 속도는 여기서 잃을 수 있습니다.
Andrey Tarantsov

+1. 이해가 안 돼요 값 유형 인 경우 각 메소드마다 고유 한 값 (별도의 값)을 갖습니다. 그렇다면 메소드가 취소가 호출되었는지 어떻게 알 수 있습니까? TokenSource에 대한 참조가 없습니다. 볼 수있는 모든 방법은 "5"와 같은 로컬 값 유형입니다. 설명 할 수 있습니까?
Royi Namir

1
@RoyiNamir : 각 CancellationToken 타입 CancellationTokenSource의 개인 기준 "m_source"가
Andreyul

2

CancellationTokenSource'일'이 문제가 어떤 이유로 취소입니다. CancellationToken발행 한 모든에 취소를 '파견'하는 방법이 필요합니다 . 예를 들어 ASP.NET은 요청이 중단 될 때 작업을 취소 할 수 있습니다. 각 요청에는 CancellationTokenSource취소 한 모든 토큰에 취소가 전달됩니다.

이것은 단위 테스트 BTW에 좋습니다-자신 만의 취소 토큰 소스를 만들고, 토큰을 얻고 Cancel, 소스를 호출 하고, 취소를 처리해야하는 코드에 토큰을 전달하십시오.


감사합니다 -하지만 모두 책임은 (이 되어 매우 관련) 같은 클래스에 제공 할 수있다. 그들이 왜 분리되어 있는지 실제로 설명하지는 않습니다.
Andrey Tarantsov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.