2013-05-29 1 views
1

매우 큰 기존 동기 코드베이스에서 async/await을 사용하려고합니다. 이 코드베이스에는 전역 컨텍스트가 있지만 동기 컨텍스트에서는 kludgy이면 제대로 작동하지만 async/await의 비동기 컨텍스트에서는 작동하지 않습니다.async/await를 사용할 때, 지속은 언제 발생합니까?

그래서 두 가지 옵션은 매우 크고 시간이 많이 걸리는 작업이 될 수있는 글로벌 컨텍스트를 배제하거나 지속성이 실행될 때 영리한 작업을 수행하는 것으로 보입니다.

async/await 및 continuations을 더 잘 이해하기 위해 여기에 표시된 테스트 프로그램을 만들었습니다. 여기에 표시됩니다.

// A method to simulate an Async read of the database. 
    private static Task ReadAsync() 
    { 
     return Task.Factory.StartNew(() => 
     { 
      int max = int.MaxValue/2; 
      for (int i = 0; i < max; ++i) 
      { 
      } 
     }); 
    } 

    // An async method that creates several continuations. 
    private static async Task ProcessMany(int i) 
    { 
     Console.WriteLine(string.Format("{0} {1}", i.ToString(), 0)); 

     await ReadAsync(); 

     Console.WriteLine(string.Format("{0} {1}", i.ToString(), 1)); 

     await ReadAsync(); 

     Console.WriteLine(string.Format("{0} {1}", i.ToString(), 2)); 

     await ReadAsync(); 

     Console.WriteLine(string.Format("{0} {1}", i.ToString(), 3)); 
    } 


    public static void Main(string[] args) 
    { 
     Queue<Task> queue = new Queue<Task>(); 

     for (int i = 0; i < 10; ++i) 
     { 
      queue.Enqueue(ProcessMany(i)); 
     } 

     // Do some synchonous processing... 
     Console.WriteLine("Processing... "); 

     for (int i = 0; i < int.MaxValue; ++i) 
     { 
     } 

     Console.WriteLine("Done processing... "); 

     queue.Dequeue().Wait(); 
    } 

모든 비동기에 대한 책을 읽은 후/기다리고 있습니다 내 이해 ".. 처리"를 연속 요청 아무도 사이에 일어나지 않을 것이라는 것와 "처리를 마쳤습니다 ..."WriteLines합니다.

다음은 몇 가지 출력 예입니다.

0 0 
1 0 
2 0 
3 0 
4 0 
5 0 
6 0 
7 0 
8 0 
9 0 
Processing... 
3 1 
2 1 
7 1 
6 1 
0 1 
4 1 
5 1 
1 1 
6 2 
3 2 
Done processing... 
7 2 
2 2 
0 2 
4 2 
5 2 
1 2 
6 3 
3 3 
7 3 
2 3 
0 3 

나는 첫 번째가 완료 동안 잠재적으로 여러 연속성을 양보하는 프로그램의 마지막에 하나의 대기()를 기대하지만, 나는 어떤 연속 요청은 "처리 사이에 실행할 수있는 방법을 이해하지 않습니다 .. . "및"처리 완료 ... ". Console.WriteLine 메서드에서 yield 나 something이있을 수 있다고 생각했기 때문에 완전히 대체했지만 출력을 변경하지 않았습니다.

분명히 비동기/대기에 대한 이해가 부족합니다. 변수를 단순히 증분 할 때 어떻게 계속 될 수 있습니까? 컴파일러 나 CLR이 일종의 마법을 주입하고 있습니까?

비동기/대기 및 계속성에 대한 이해를 돕기 위해 미리 도움을 주셔서 감사합니다.

편집 : 당신이 스티븐에 의해 코멘트에 따라이 방법 샘플 코드를 편집하는 경우

, 무슨 일이 훨씬 더 분명하다.

// An async method that creates several continuations. 
    private static async Task ProcessMany(int i) 
    { 
     Console.WriteLine(string.Format("{0} {1} {2}", i.ToString(), 0, Thread.CurrentThread.ManagedThreadId)); 

     await ReadAsync(); 

     Console.WriteLine(string.Format("{0} {1} {2}", i.ToString(), 1, Thread.CurrentThread.ManagedThreadId)); 

     await ReadAsync(); 

     Console.WriteLine(string.Format("{0} {1} {2}", i.ToString(), 2, Thread.CurrentThread.ManagedThreadId)); 

     await ReadAsync(); 

     Console.WriteLine(string.Format("{0} {1} {2}", i.ToString(), 3, Thread.CurrentThread.ManagedThreadId)); 
    } 


    public static void Main(string[] args) 
    { 
     Queue<Task> queue = new Queue<Task>(); 

     for (int i = 0; i < 10; ++i) 
     { 
      queue.Enqueue(ProcessMany(i)); 
     } 

     // Do some synchonous processing... 
     Console.WriteLine("Processing... {0}", Thread.CurrentThread.ManagedThreadId); 

     for (int i = 0; i < int.MaxValue; ++i) 
     { 
     } 

     Console.WriteLine("Done processing... {0}", Thread.CurrentThread.ManagedThreadId); 

     queue.Dequeue().Wait(); 
    } 

출력 :

0 0 9 
1 0 9 
2 0 9 
3 0 9 
4 0 9 
5 0 9 
6 0 9 
7 0 9 
8 0 9 
9 0 9 
Processing... 9 
4 1 14 
3 1 13 
2 1 12 
5 1 15 
0 1 10 
6 1 16 
1 1 11 
7 1 17 
4 2 14 
3 2 13 
0 2 10 
6 2 16 
2 2 12 
5 2 15 
Done processing... 9 
1 2 11 
7 2 17 
0 3 10 
4 3 14 

답변

5

당신이 SynchronizationContext 또는 TaskScheduler는, 다음의 연속 요청은 스레드 풀 스레드에서 실행됩니다 현재이없는 경우 (별도 메인 스레드에서). 콘솔 앱의 경우이지만 WinForms/WPF/ASP.NET에서는 매우 다른 동작을 보게됩니다.

은 사용자 정의 TaskScheduler을 사용하여 계속 예약을 제어 할 수 있지만 그다지 많은 효과가 없을 것입니다. 전 세계적인 문제가 무엇인지 명확하지 않지만 SemaphoreSlim과 같은 대안을 고려하십시오.

+0

감사합니다. 게시 한 시간과 거의 같은 시간에이 사실을 알았습니다. 샘플 코드를 편집 했으므로 훨씬 더 이해가됩니다. – Ashley

+0

즉, 비 UI 컨텍스트에서 연속성은 스레드로부터 안전해야한다는 의미입니까? async/await의 큰 이점은 스레드가 메인 스레드에서 모두 실행되므로 스레드 안전성에 대해 걱정할 필요가 없다는 것입니다. : \ – Ashley

+0

내 글로벌 상태는 데이터베이스 연결과 데이터베이스 트랜잭션과 관련이 있습니다. 매우 복잡한 방식으로 코드베이스에 포함되어 있기 때문에, 분해는 매우 어려울 것입니다. 새로운 언어로 여러 포트를 통과 한 코드베이스는 30 년 전입니다. :) – Ashley

0

ProcessMany에서 다음 줄을 호출하면 즉시 ProcessMany에 대한 각 호출이 별도의 스레드 풀에있는 별도의 스레드에서 실행되기 시작합니다.

await ...; 

그래서 "처리 중"출력 전에 여러 통화가 표시됩니다. 따라서 10 개의 ProcessMany 호출을 모두 실행하는 동안 큰 루프를 실행하기 시작합니다. 커다란 루프가 주 스레드에서 실행 중이기 때문에 10 개의 ProcessMany 호출은 스레드에서 계속 실행되어 추가 출력물을 생성합니다.ProcessMany 호출이 메인 스레드 루프가 끝나기 전에 실행을 끝내지 않는 것처럼 보이므로 "처리 완료"출력 후에 더 많은 결과를 계속 내 보냅니다.

나는 당신을 위해 일의 순서를 명확히하기를 바랍니다.

관련 문제