문제

현재 순차적으로 실행될 큐에 많은 수의 C# 계산(메서드 호출)이 있습니다.각 계산에서는 대기 시간이 긴 서비스(네트워크, 디스크...)를 사용합니다.

저는 모노 코루틴을 사용하여 이전 계산이 대기 시간이 긴 서비스가 반환되기를 기다리는 동안 계산 대기열의 다음 계산을 계속할 수 있도록 하려고 했습니다.그러나 나는 Mono 코루틴에 의존하지 않는 것을 선호합니다.

대기 시간이 긴 서비스가 반환될 때까지 기다리는 동안 추가 계산을 처리할 수 있도록 순수 C#으로 구현할 수 있는 디자인 패턴이 있습니까?

감사해요

업데이트:

엄청난 수(>10000)의 작업을 실행해야 하며 각 작업은 지연 시간이 긴 서비스를 사용하게 됩니다.Windows에서는 그렇게 많은 스레드를 생성할 수 없습니다.

업데이트:

기본적으로 Stackless Python에서 태스크릿의 장점(다음과 같이)을 에뮬레이션하는 디자인 패턴이 필요합니다(http://www.스택리스.com/)

  1. 엄청난 양의 작업
  2. 작업이 대기열의 다음 작업을 차단하는 경우 실행됩니다.
  3. 낭비되는 CPU 사이클 없음
  4. 작업 간 전환을 최소화하는 오버헤드
도움이 되었습니까?

해결책

IEnumerable을 사용하여 협력적 마이크로스레딩을 시뮬레이션할 수 있습니다.안타깝게도 이는 차단 API에서는 작동하지 않으므로 폴링할 수 있거나 신호 전달에 사용할 수 있는 콜백이 있는 API를 찾아야 합니다.

방법을 고려해보세요

IEnumerable Thread ()
{
    //do some stuff
    Foo ();

    //co-operatively yield
    yield null;

    //do some more stuff
    Bar ();

    //sleep 2 seconds
    yield new TimeSpan (2000);
}

C# 컴파일러는 이를 상태 머신으로 풀지만 모양은 협력적인 마이크로스레드의 모습입니다.

패턴은 매우 간단합니다.모든 활성 IEnumerator 목록을 유지하는 "스케줄러"를 구현합니다.목록을 순환하면서 MoveNext()를 사용하여 각 목록을 "실행"합니다.MoveNext 값이 false이면 스레드가 종료되고 스케줄러가 이를 목록에서 제거합니다.true인 경우 스케줄러는 Current 속성에 액세스하여 스레드의 현재 상태를 확인합니다.TimeSpan인 경우 스레드는 절전 모드를 원하며 스케줄러는 해당 스레드를 절전 시간 범위가 끝나면 기본 목록으로 다시 플러시할 수 있는 일부 대기열로 이동했습니다.

다른 반환 개체를 사용하여 다른 신호 메커니즘을 구현할 수 있습니다.예를 들어 일종의 WaitHandle을 정의합니다.스레드가 이들 중 하나를 생성하면 핸들이 신호를 받을 때까지 대기 대기열로 이동할 수 있습니다.또는 대기 핸들 배열을 생성하여 WaitAll을 지원할 수도 있습니다.우선순위를 구현할 수도 있습니다.

나는 약 150LOC에서 이 스케줄러를 간단하게 구현했지만 아직 코드를 블로그에 올리지는 못했습니다.이는 PhyreSharp PhyreEngine 래퍼(공개되지 않음)를 위한 것인데, 데모 중 하나에서 수백 개의 문자를 제어하는 ​​데 꽤 잘 작동하는 것 같습니다.우리는 Unity3D 엔진에서 개념을 차용했습니다. Unity3D 엔진에는 사용자 관점에서 이를 설명하는 온라인 문서가 있습니다.

다른 팁

나는 다음을 사용하는 것이 좋습니다 스레드 풀 작업 대기열에서 제공되는 활성 작업 목록을 사용하여 관리 가능한 배치로 대기열에서 여러 작업을 한 번에 실행합니다.

이 시나리오에서 기본 작업자 스레드는 처음에 대기열의 N개 작업을 활성 작업 목록으로 팝하여 스레드 풀로 발송됩니다(대개 다음을 사용). 대기열사용자작업항목), 여기서 N은 스레드 풀에 과부하가 걸리지 않고, 스레드 예약 및 동기화 비용으로 인해 앱이 중단되거나, 각 작업의 결합된 I/O 메모리 오버헤드로 인해 사용 가능한 메모리를 빨아들이지 않는 관리 가능한 양을 나타냅니다.

작업이 작업자 스레드에 완료 신호를 보낼 때마다 활성 작업 목록에서 해당 작업을 제거하고 실행할 작업 대기열에 다음 작업을 추가할 수 있습니다.

이렇게 하면 대기열에서 N개 작업의 롤링 세트를 가질 수 있습니다.N을 조작하여 성능 특성에 영향을 미치고 특정 상황에 가장 적합한 것이 무엇인지 찾을 수 있습니다.

궁극적으로 하드웨어 작업(디스크 I/O 및 네트워크 I/O, CPU)으로 인해 병목 현상이 발생하므로 작을수록 좋다고 생각합니다.디스크 I/O에서 작동하는 두 개의 스레드 풀 작업은 하나보다 빠르게 실행되지 않을 가능성이 높습니다.

또한 활성 작업 목록을 특정 유형의 특정 작업 수로 제한하여 크기와 내용에 유연성을 구현할 수도 있습니다.예를 들어 코어가 4개인 시스템에서 실행 중인 경우 가장 높은 성능의 구성은 하나의 디스크 바인딩 작업 및 네트워크 작업과 함께 동시에 실행되는 4개의 CPU 바인딩 작업이라는 것을 알 수 있습니다.

디스크 IO 작업으로 분류된 작업 하나가 이미 있는 경우 다른 디스크 IO 작업을 추가하기 전에 해당 작업이 완료될 때까지 기다리도록 선택할 수 있으며 그 사이에 CPU 바인딩 또는 네트워크 바인딩 작업을 예약하도록 선택할 수 있습니다.

이것이 의미가 있기를 바랍니다!

추신:작업 순서에 의존하는 부분이 있나요?

꼭 확인해 보세요. 동시성 및 조정 런타임.샘플 중 하나가 귀하가 말하는 내용을 정확하게 설명합니다.대기 시간이 긴 서비스를 호출하면 CCR은 기다리는 동안 다른 작업이 실행되도록 효율적으로 허용합니다.요청하면 모든 코어를 사용하지만 각 작업에 대해 스레드를 생성할 필요가 없기 때문에 엄청난 수의 작업을 처리할 수 있습니다.

이것은 멀티스레드 처리의 일반적인 사용이 아닌가요?

Reactor와 같은 패턴을 살펴보세요. 여기

써서 사용하기 비동기 IO 충분할 수도 있습니다.

이는 디자인에 강력한 구조가 없으면 난잡하고 디버깅하기 어려운 코드로 이어질 수 있습니다.

이것을 살펴봐야 합니다:

http://www.replicator.org/node/80

이것은 정확히 당신이 원하는 것을 할 것입니다.하지만 그것은 해킹입니다.

.NET 구현과 관련된 "Reactive" 패턴(다른 포스터에서 언급한 대로)에 대한 추가 정보입니다.일명 "Linq to Events"

http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html

-오이신

실제로 작업에 하나의 스레드를 사용하면 게임에서 패배하게 됩니다.Node.js가 수많은 연결을 지원할 수 있는 이유를 생각해 보세요.비동기 IO로 몇 개의 스레드 사용!!!비동기 및 대기 기능이 이에 도움이 될 수 있습니다.

foreach (var task in tasks)
{
    await SendAsync(task.value);
    ReadAsync(); 
}

SendAsync() 및 ReadAsync()는 비동기 IO 호출에 대한 가짜 함수입니다.

작업 병렬성 역시 좋은 선택이다.하지만 어느 것이 더 빠른지는 잘 모르겠습니다.귀하의 경우 두 가지를 테스트 할 수 있습니다.

네, 물론 가능합니다.제공한 람다를 콜백하고 대기열로 이동하는 디스패처 메커니즘을 구축하기만 하면 됩니다.제가 Unity로 작성하는 모든 코드는 이 접근 방식을 사용하며 코루틴은 절대 사용하지 않습니다.나는 이를 제거하기 위해 WWW와 같은 코루틴을 사용하는 메서드를 래핑합니다.이론적으로 코루틴은 오버헤드가 적기 때문에 더 빠를 수 있습니다.실제로 그들은 매우 사소한 작업을 수행하기 위해 언어에 새로운 구문을 도입하고 더욱이 코루틴에서 오류가 발생하면 스택 추적을 제대로 따라갈 수 없습니다. 왜냐하면 모든 것이 ->다음이기 때문입니다.그런 다음 다른 스레드의 대기열에 있는 작업을 실행하는 기능을 구현해야 합니다.그러나 최신 .net에는 병렬 함수가 있으며 본질적으로 유사한 기능을 작성하게 됩니다.실제로는 많은 코드 줄이 아닐 것입니다.

관심 있는 사람이 있으면 코드를 보내겠습니다. 나에게는 코드가 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top