이것은 화재 및 잊어 버린 작업이 일반적으로 좋지 않은 이유입니다. 그들은 finally
블럭 안에 과 함께 try/catch
안에 추가를 래핑하지 않으므로 GetConsumingEnumerable
의 열거 자에있는 MoveNext
에 대한 호출이 결국 무기한 차단된다는 것을 의미하므로 사용자의 경우에는 아이디어가 더 나빠집니다. 이는 나쁘지는 않습니다.
C# 경계 내에서 완전히 작업하는 경우 솔루션을 간단하게 만들 수 있습니다. 문제를보다 효과적으로 구분할 수 있습니다. BlockingCollection
비트를 제거하고 그것이 속한 곳, 소비자 (클라이언트) 또는 중간 파이프 라인 처리 단계 (궁극적으로 달성하려는 것)에서 실행하십시오. 생산자가 던진 예외의 귀하의 GetData
서명은 동일하게 유지하지만, 전체 예외 전파에 열거하는 간단한 차단된다 :
이제
var records = new BlockingCollection<DataRecord>();
var producer = Task.Run(() =>
{
try
{
foreach (var record in GetData("http://foo.com/Service", 22))
{
// Hand over the record to the
// consumer and continue enumerating.
records.Add(record);
}
}
finally
{
// This needs to be called even in
// exceptional scenarios so that the
// consumer task does not block
// indefinitely on the call to MoveNext.
records.CompleteAdding();
}
});
var consumer = Task.Run(() =>
{
foreach (var record in records.GetConsumingEnumerable())
{
// Do something with the record yielded by GetData.
// This runs in parallel with the producer,
// So you get concurrent download and processing
// with a safe handover via the BlockingCollection.
}
});
await Task.WhenAll(producer, consumer);
당신은 당신의 케이크가 있고 그것을 먹을 수
public static IEnumerable<DataRecord> GetData(String serverAddress, Int64 dataID)
{
Boolean isMoreData = false;
do
{
// make server request and process response
// this block can throw
yield return response.record;
isMoreData = response.IsMoreData;
}
while (isMoreData);
}
그런 다음 파이프 라인은 다음과 같습니다 레코드가 GetData
에 의해 산출 될 때 처리가 병렬로 발생하고 생산자 작업에 await
이 예외를 전파하는 반면 finally
내부에 CompleteAdding
을 호출하면 소비자가 불명확 한 블로킹 상태에 빠지지 않도록 보장합니다. 어.
C++로 작업 했으므로 위의 내용은 어느 정도까지 적용 할 수 있습니다 (즉, C++에서 파이프 라인을 다시 구현하는 것이 옳은 일입니다). 그러나 구현이 너무 예쁘지는 않을 것입니다. 당신은 함께 갔지만 관찰되지 않은 작업으로 인해 해킹 된 것처럼 느껴질지라도 귀하의 대답은 선호되는 해결책 일 수 있습니다. 실제로 이이 될 시나리오를 생각해 볼 수는 없습니다. CompleteAdding
은 항상 새로 도입 된 try/catch
으로 인해 호출됩니다.
분명히 다른 해결책은 처리 코드를 C# 프로젝트로 옮기는 것입니다. 이는 아키텍처에 따라 가능하지 않을 수도 있습니다.
작업 공장 및 작업 계속 옵션을 고려 했습니까? – Darek