두 개의 비동기 작업을 병렬로 실행하고 .NET 4.5에서 결과 수집


116

.NET 4.5로 작업하는 것이 간단하다고 생각한 것을 얻으려고 잠시 노력해 왔습니다.

두 개의 장기 실행 작업을 동시에 시작하고
최상의 C # 4.5 (RTM) 방식으로 결과를 수집하고 싶습니다.

다음은 작동하지만 나는 그것을 좋아하지 않습니다.

  • 다른 방법을 Sleep사용할 수 있도록 비동기 메서드가 되고 싶습니다.await
  • 그냥 서투르게 보입니다. Task.Run()
  • 나는 이것이 새로운 언어 기능을 전혀 사용하지 않는다고 생각합니다!

작동 코드 :

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Task.Run(() => Sleep(5000));    
    var task2 = Task.Run(() => Sleep(3000));

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for a total of " + totalSlept + " ms");
}

private static int Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    Console.WriteLine("Sleeping for " + ms + " FINISHED");
    return ms;
}

작동하지 않는 코드 :

업데이트 : 이것은 실제로 작동하며 올바른 방법입니다. 유일한 문제는 Thread.Sleep

이 코드는 호출이 Sleep(5000)즉시 작업 실행을 시작하기 때문에 Sleep(1000)작동하지 않으므로 완료 될 때까지 실행되지 않습니다. 이것은 사실 이며 너무 빨리 사용 하거나 전화 하지 않더라도 마찬가지 Sleep입니다 .asyncawait.Result

두 작업 을 호출 할 수 있도록 메서드 Task<T>를 호출하여 실행되지 않는 방법이있을 수 있다고 생각 했지만 비동기 메서드를 호출하여 a를 얻는 방법을 알 수 없습니다 .asyncStart()Task<T>

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Sleep(5000);    // blocks
    var task2 = Sleep(1000);

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
}

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    return ms;
}

참고 : Go를 비동기 메서드로 만들어도 차이가 없습니다
Simon_Weaver 2012-09-09

3
await 키워드가없는 Sleep 메서드가 동기 적이기 때문에 task1.Resultnot at 에서 차단이 발생 var task1 = Sleep(5000)합니다.
Arvis

답변:


86

비동기 프로그래밍을 위해 Sleep 대신 Task.Delay를 사용한 다음 Task.WhenAll을 사용하여 작업 결과를 결합해야합니다. 작업은 병렬로 실행됩니다.

public class Program
    {
        static void Main(string[] args)
        {
            Go();
        }
        public static void Go()
        {
            GoAsync();
            Console.ReadLine();
        }
        public static async void GoAsync()
        {

            Console.WriteLine("Starting");

            var task1 = Sleep(5000);
            var task2 = Sleep(3000);

            int[] result = await Task.WhenAll(task1, task2);

            Console.WriteLine("Slept for a total of " + result.Sum() + " ms");

        }

        private async static Task<int> Sleep(int ms)
        {
            Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount);
            await Task.Delay(ms);
            Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount);
            return ms;
        }
    }

11
이것은 대단한 대답입니다 ...하지만 실행하기 전까지는 이것이 잘못된 대답이라고 생각했습니다. 그때 나는 이해했다. 실제로 5 초만에 실행됩니다. 트릭은 작업을 즉시 기다리지 않고 대신 Task.WhenAll을 기다리는 것입니다.
Tim Lovell-Smith

113
async Task<int> LongTask1() { 
  ...
  return 0; 
}

async Task<int> LongTask2() { 
  ...
  return 1; 
}

...
{
   Task<int> t1 = LongTask1();
   Task<int> t2 = LongTask2();
   await Task.WhenAll(t1,t2);
   //now we have t1.Result and t2.Result
}

2
나는 당신이 t1, t2를 Task로 선언했기 때문에 +1합니다.
Minime 2013

12
이 솔루션을 사용하려면 Go 메서드도 비동기식이어야합니다. 즉, 비동기식 기능을 제공합니다. 호출자의 Go메서드가 동기식이지만 두 개의 독립적 인 작업을 비동기식으로 완료하려는 요청자 사례와 같은 것을 원하면 (즉, 두 작업 모두 다른 작업보다 먼저 완료 할 필요가없고 실행이 계속되기 전에 둘 다 완료되어야 Task.WaitAll함) 더 좋을 것입니다. await 키워드가 필요하지 않으므로 호출 Go메서드가 자체적으로 비동기 일 필요가 없습니다 . 두 가지 접근 방식 모두 더 좋지 않으며 목표가 무엇인지의 문제 일뿐입니다.
AaronLS

1
무효 방법 : async void LongTask1() {...}Task.Result 속성이 없습니다. 다음과 같은 경우 T없이 Task를 사용 async Task LongTask1()합니다..
Arvis

두 작업 중 하나에서 결과를 얻지 못했습니다. 그래서 나는 그것을로 변경 Task<TResult> t1 = LongTask1();했고 이제 나는 t1.Result. <TResult>결과의 반환 유형입니다. return <TResult>이 작업을 수행하려면 방법에이 필요합니다 .
gilu

1
정말 간단한 일을하고 있고 추가 변수 t1t2변수를 원하지 않는 경우 new Task(...). 예 : int int1 = 0; await Task.WhenAll(new Task(async () => { int1 = await LongTask1(); }));. 이 접근 방식의 한 가지 문제점은 컴파일러가 변수가 할당되었음을 인식하지 못하고 초기 값을 제공하지 않으면 할당되지 않은 것으로 처리한다는 것입니다.
Robert Dennis

3

귀하의 Sleep방법은 비동기이지만 Thread.Sleep그렇지 않습니다. 비동기의 전체 개념은 여러 스레드를 시작하는 것이 아니라 단일 스레드를 재사용하는 것입니다. Thread.Sleep에 대한 동기 호출을 사용하여 차단했기 때문에 작동하지 않습니다.

나는 Thread.Sleep그것이 당신이 실제로하고 싶은 일의 단순화 라고 가정하고 있습니다. 실제 구현을 비동기 메서드로 코딩 할 수 있습니까?

여러 개의 동기 차단 호출을 실행해야하는 경우 다른 곳을보십시오.


감사합니다 Richard-예 제가 실제로 서비스 콜을 사용할 때 예상대로 작동하는 것 같습니다
Simon_Weaver 2012-09-09

그런 다음 비동기를 실행하는 방법? 나는 그 파일에 대한 파일 전환 및 대기의 많이 할 응용 프로그램, 약 5 초, 그리고 다른 프로세스를 가질 때 "모두를 위해 할 때"먼저 첫 번째, 그 다음 두 번째, 내가 말한에도 불구하고 실행 var x = y(), 그리고 var x=await y()y().wait()아직도 그것을 아직 끝까지 기다렸다가 비동기로 처리되지 않으면 어떻게해야합니까? 참고가 Y는 비동기로 장식하고, 나는 그것이 할 때 모든 내에서 모든 것을 할 것으로 기대된다하지 못했습니다이 할당되고, 편집에 내 파트너가 말했다에 난 그냥 때의 시도하자 Task.Factory, 그는 내가 탈옥 때 일했다 이 클래스의 측면
deadManN

2

이 점에 답하려면 :

Sleep이 비동기 메서드가되기를 원하므로 다른 메서드를 기다릴 수 있습니다.

다음 Sleep과 같이 함수를 다시 작성할 수 있습니다 .

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    var task = Task.Run(() => Thread.Sleep(ms));
    await task;
    Console.WriteLine("Sleeping for " + ms + "END");
    return ms;
}

static void Main(string[] args)
{
    Console.WriteLine("Starting");

    var task1 = Sleep(2000);
    var task2 = Sleep(1000);

    int totalSlept = task1.Result +task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
    Console.ReadKey();
}

이 코드를 실행하면 다음이 출력됩니다.

Starting
Sleeping for 2000
Sleeping for 1000
*(one second later)*
Sleeping for 1000END
*(one second later)*
Sleeping for 2000END
Slept for 3000 ms

2

그건 주말 하세요!

    public async void Go()
    {
        Console.WriteLine("Start fosterage...");

        var t1 = Sleep(5000, "Kevin");
        var t2 = Sleep(3000, "Jerry");
        var result = await Task.WhenAll(t1, t2);

        Console.WriteLine($"My precious spare time last for only {result.Max()}ms");
        Console.WriteLine("Press any key and take same beer...");
        Console.ReadKey();
    }

    private static async Task<int> Sleep(int ms, string name)
    {
            Console.WriteLine($"{name} going to sleep for {ms}ms :)");
            await Task.Delay(ms);
            Console.WriteLine("${name} waked up after {ms}ms :(";
            return ms;
    }

0

이 기사는 많은 것을 설명하는 데 도움이되었습니다. FAQ 스타일입니다.

비동기 / 대기 FAQ

이 부분 Thread.Sleep은 동일한 원래 스레드에서 실행되는 이유를 설명합니다. 이로 인해 초기 혼란이 발생합니다.

"async"키워드로 인해 메서드 호출이 ThreadPool에 대기합니까? 새 스레드를 만들려면? 화성에 로켓 우주선을 발사하려면?

아니, 아니. 이전 질문을 참조하십시오. "async"키워드는 메서드 내부에서 "await"를 사용할 수 있음을 컴파일러에 표시하여 메서드가 대기 지점에서 일시 중단되고 대기중인 인스턴스가 완료 될 때 비동기 적으로 실행이 재개 될 수 있습니다. 이것이 "async"로 표시된 메서드 내부에 "awaits"가 없으면 컴파일러가 경고를 발행하는 이유입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.