서로 의존하지 않는 비동기 REST 서비스가 거의 없습니다. 즉, Service1의 응답을 “대기”하는 동안 Service2, Service3 등을 호출 할 수 있습니다.
예를 들어 아래 코드를 참조하십시오.
var service1Response = await HttpService1Async();
var service2Response = await HttpService2Async();
// Use service1Response and service2Response
이제 service2Response
의존하지 않으며 service1Response
독립적으로 가져올 수 있습니다. 따라서 두 번째 서비스를 호출하기 위해 첫 번째 서비스의 응답을 기다릴 필요가 없습니다.
Parallel.ForEach
CPU 바인딩 작업이 아니기 때문에 여기서 사용할 수 없다고 생각 합니다.
이 두 연산을 병렬로 호출하기 위해 use 호출 할 수 Task.WhenAll
있습니까? 내가 사용하는 한 가지 문제 Task.WhenAll
는 결과를 반환하지 않는다는 것입니다. 모든 작업이 이미 완료되었고 응답을 가져 오기 위해 필요한 모든 작업을 수행 task.Result
한 후 호출 한 후 호출 할 수 있습니다 Task.WhenAll
.
샘플 코드 :
var task1 = HttpService1Async();
var task2 = HttpService2Async();
await Task.WhenAll(task1, task2)
var result1 = task1.Result;
var result2 = task2.Result;
// Use result1 and result2
이 코드는 성능면에서 첫 번째 코드보다 낫습니까? 다른 방법을 사용할 수 있습니까?
답변
Task.WhenAll 사용하면 하나의 문제는 결과를 반환하지 않는다는 것입니다.
그러나 그것은 수행 결과를 반환합니다. 그것들은 모두 공통 유형의 배열에있을 것이므로 결과를 사용하는 것이 항상 유용한 것은 아닙니다 . 결과를 원하는 배열에 해당하는 항목을 찾아서 Task
잠재적으로 캐스트해야합니다. 실제 유형 이므로이 컨텍스트에서 가장 쉽고 읽기 쉬운 접근법은 아니지만 모든 작업의 모든 결과를 원할 때 공통 유형은 처리하려는 유형입니다. .
결과를 가져 오려면 Task.WhenAll을 호출 한 후 task.Result를 호출 할 수 있습니다. 모든 작업이 이미 완료되어 응답을 가져와야하기 때문에 모두?
네, 그렇게 할 수 있습니다. 당신은 await
그들 도 할 수 있습니다 ( await
결함이있는 작업에서 예외를 풀지 Result
만 집계 예외를 throw하지만 그렇지 않으면 동일합니다).
이 코드는 성능면에서 첫 번째 코드보다 낫습니까?
두 작업을 동시에 수행하는 것이 아니라 두 작업을 동시에 수행합니다. 그것이 더 나은지 나쁜지는 기본 작업이 무엇인지에 달려 있습니다. 기본 작업이 “디스크에서 파일 읽기”인 경우 디스크 헤드가 하나만 있고 주어진 시간에 한 곳에만있을 수 있으므로 병렬로 수행하는 것이 느려질 수 있습니다. 두 파일 사이를 뛰어 다니는 것은 한 파일을 읽은 다음 다른 파일을 읽는 것보다 느립니다. 반면, 작업이 “일부 네트워크 요청을 수행”하는 경우 (여기서와 같이) 응답을 기다릴 수 있기 때문에 작업이 더 빠를 수 있습니다 (최소한 특정 수의 동시 요청 수). 보류중인 다른 네트워크 요청이있을 때와 마찬가지로 다른 네트워크 컴퓨터에서도 빠르게. 당신이 그것을 알고 싶다면
다른 방법을 사용할 수 있습니까?
당신이 알고있는 당신에게하지 않는 것이 중요합니다 경우 모든 할 수 있습니다 단순히 당신이 아니라 바로 첫 번째보다 병행하고있는 모든 작업 사이에 던져진 예외를 await
하지 않고 작업을 WhenAll
전혀. 있는 유일한 방법은 WhenAll
당신이 데 제공 AggregateException
하지 않고 첫 번째 고장 난 작업을 명중 할 때 던지는 것보다, 모든 고장 난 작업에서 모든 단일 제외합니다. 다음과 같이 간단합니다.
var task1 = HttpService1Async();
var task2 = HttpService2Async();
var result1 = await task1;
var result2 = await task2;
답변
다음은 SemaphoreSlim을 사용하고 최대 병렬 처리 수준을 설정할 수있는 확장 방법입니다.
/// <summary>
/// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type of IEnumerable</typeparam>
/// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
/// <param name="action">an async <see cref="Action" /> to execute</param>
/// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
/// Must be grater than 0</param>
/// <returns>A Task representing an async operation</returns>
/// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
public static async Task ForEachAsyncConcurrent<T>(
this IEnumerable<T> enumerable,
Func<T, Task> action,
int? maxDegreeOfParallelism = null)
{
if (maxDegreeOfParallelism.HasValue)
{
using (var semaphoreSlim = new SemaphoreSlim(
maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
{
var tasksWithThrottler = new List<Task>();
foreach (var item in enumerable)
{
// Increment the number of currently running tasks and wait if they are more than limit.
await semaphoreSlim.WaitAsync();
tasksWithThrottler.Add(Task.Run(async () =>
{
await action(item).ContinueWith(res =>
{
// action is completed, so decrement the number of currently running tasks
semaphoreSlim.Release();
});
}));
}
// Wait for all tasks to complete.
await Task.WhenAll(tasksWithThrottler.ToArray());
}
}
else
{
await Task.WhenAll(enumerable.Select(item => action(item)));
}
}
샘플 사용법 :
await enumerable.ForEachAsyncConcurrent(
async item =>
{
await SomeAsyncMethod(item);
},
5);
답변
둘 중 하나를 사용할 수 있습니다
Parallel.Invoke(() =>
{
HttpService1Async();
},
() =>
{
HttpService2Async();
});
또는
Task task1 = Task.Run(() => HttpService1Async());
Task task2 = Task.Run(() => HttpService2Async());
//If you wish, you can wait for a particular task to return here like this:
task1.Wait();