2016-11-10 11 views
2

비동기 흐름을 통해 1000 개의 요소 목록을 데이터베이스에 기록해야한다고 가정합니다. 비동기 삽입 문을 1000 번 기다리는 것이 더 좋습니까 아니면 Task.Run 문에 캡슐화 된 단일 동기 메서드에 1000 개의 삽입을 모두 래핑하여 한 번만 기다리십니까?많은 사람들이 비동기 메서드를 기다리고 있거나 랩핑 Task.Run을 기다리는 사람이 하나 있습니다.

예를 들어 SqlCommand에는 모든 방법이 그의 async 버전과 결합되어 있습니다. 이 경우 insert 문이 있으므로 ExecuteNonQuery 또는 ExecuteNonQueryAsync을 호출 할 수 있습니다.

종종 async/await 가이드 라인에서 비동기 버전을 사용할 수있는 경우이를 사용해야합니다. 다음과 같이 작성한다고 가정 해 보겠습니다.

async Task Save(IEnumerable<Savable> savables) 
{ 
    foreach(var savable in savables) 
    { 
     //create SqlCommand somehow 
     var sqlCmd = CreateSqlCommand(savable); 

     //use asynchronous version 
     await sqlCmd.ExecuteNonQueryAsync(); 
    } 
} 

이 코드는 매우 명확합니다. 그러나 대기중인 부분에서 나올 때마다 UI 스레드에서 반환하고 다음 백그라운드 스레드에서 다시 발생하는 등 기다리고 있습니다 (그렇지 않습니까?). UI 슬록이 await의 계속에 의해 연속적으로 인터럽트되어 다음 foreach 사이클을 실행하고 UI의 일부가 정지되면 사용자가 약간의 지연을 볼 수 있음을 의미합니다. 이런 식으로

async Task Save(IEnumerable<Savable> savables) 
{ 
    await Task.Run(() => 
    { 
     foreach(var savable in savables) 
     { 
      //create SqlCommand somehow 
      var sqlCmd = CreateSqlCommand(savable); 

      //use synchronous version 
      sqlCmd.ExecuteNonQuery(); 
     } 
    }); 
} 

, 전체 foreach는 UI 스레드와 보조 일 사이에 연속 스위치없이, 보조 스레드에서 실행됩니다

는 나는이 같은 더 나은 코드를 작성하는 경우 알고 싶어요. 이것은 UI 스레드가 foreach (예 : 회 전자 또는 진행률 막대)의 전체 기간 동안보기를 자유롭게 업데이트 할 수 있음을 의미합니다. 즉, 사용자가 지체를 감지하지 않습니다.

맞습니까? 또는 "비동기 도중 내내 무언가를 놓치고 있습니까?"

나는 간단한 의견 기반 답변을 찾고 있지 않다. 그런 경우에는 async/await 지침에 대한 설명을 찾고 최선의 해결 방법을 찾고있다.

편집 : 나는 this question을 읽었지만 동일하지 않습니다

. 질문은 단일 await을 비동기 방식으로 선택하고 대 Task.Run을 기다리는 것입니다. 이 질문은 1000 await 및 스레드 간의 연속 전환으로 인한 자원의 오버 헤드로 인한 결과에 대한 것입니다.

+0

가능한 [Task.Run을 사용하여 비동기 메서드를 호출하는 것이 잘못된 것 같습니까?] (http://stackoverflow.com/questions/32606485/calling-an-async-method-using-a-task-run-seems) -wrong) – Liam

+2

당신이 놓치고있는 것은 쓰레드가 극도로 비싸다는 것입니다. 그렇게 할 수 있다면 쓰레드를 만들지 않는 것이 좋습니다. – Aron

+0

'Task.Run' 메소드는 비동기식 anti 패턴 *과 동기화됩니다. Do not do – Liam

답변

5

분석에 와서

, 당신은 결과를 비동기 UI를 업데이트 할 수있는 것은 거의 정확합니다. UI 스레드에서 이러한 부담을 과대 평가하는 것처럼 보입니다. 실제로 요구되는 실제 작업은 상당히 작기 때문에 잘 유지할 수 있다는 확률이 있습니다.하지만 그렇게 할 수 없을만큼 충분히 할 수 있기 때문에 올바른 작업을 수행 할 수 있습니다. UI 스레드에서 계속 작업을 수행하지 않는 데 관심이 있습니다.

UI 스레드에 대한 모든 콜백을 피하기 위해 선호되는 방법은 물론입니다. await 작업을 수행 할 때 나머지 메서드가 실제로 원래 컨텍스트로 돌아 오지 않는다면 기다리고있는 작업의 끝에 ConfigureAwait(false)을 간단히 추가하면됩니다. 이렇게하면 현재 컨텍스트 (UI 스레드)에서 연속이 실행되지 않고 대신 스레드 풀 스레드에서 연속이 실행됩니다.

ConfigureAwait(false)을 사용하면 UI가 아닌 작업을 불필요하게 담당하지 않아도되고 스레드 풀 스레드가 필요 이상의 작업을 수행하도록 예약하지 않아도됩니다. 물론

, 실제로 는 UI 스레드에서 계속 일정을를 원하기 때문에 당신이 당신의 연속 실제로 UI 작업을 할 것입니다 후 일을 끝낼 일이, 그 방법은, ConfigureAwait(false);를 사용하지 않아야하는 경우 .

+0

이제는 ConfigureAwait을 사용할 수있는 선택권이 있으며 결과의 관점에서 볼 때 현재 2 상당히 똑같은 옵션. 이제'Task.Run' 대신'ConfigureAwait'을 사용해야하는 이유는 무엇입니까? 가독성 문제입니까? –

+1

@MassimilianoKraus 즉, 비동기 작업이 실행 된 후 스레드 풀에서 비동기 작업을 시작하고 (모든 연속을 처리 할뿐만 아니라) 스레드 풀에서 연속 작업을 실행한다는 의미입니다. 'ConfigureAwait'를 사용하면 * UI 스레드에서 실행해야하는 * 모든 메소드를 스레드 풀을 사용하면서 UI를 사용해야하는 모든 것을 허용 할 수 있으며 단순히 ConfigureAwait (false)를 추가하지 않고 UI와 상호 작용할 수 있어야합니다. – Servy

0

이것은 모두 UI에서 기대하는 바에 따라 다릅니다. UI가 유효한 상태를 유지하기 위해 완료되는 작업에 의존하는 경우 비동기 작업을 기다리거나 정상적으로 주 UI 스레드에서 호출을 사용할 수밖에 없습니다.

스레딩은 오랜 시간이 걸리지 만 호출하는 스레드가 즉시 요구하지 않거나 호출하는 스레드가 작업 결과에 의존하는 작업에 유용합니다.

작업 속도가 향상 될 수는 없으며 효율성을 위해 있습니다. 따라서 작업 스레드를 유지하고 '작업이 완료되었습니다'라는 이벤트를 발생시켜이 작업이 정상적으로 작동하는 동안 UI 작업이 계속 진행될 수 있지만 UI 스레드가 결과에 따라 다르면 기다릴 수 밖에 없습니다.당신이 이벤트 경로를 통해 이동하는 경우가

-2

두 가지 솔루션 간에는 중요한 차이점이 하나 있습니다. 첫 번째는 단일 스레드입니다 (@Servy가 제시 한대로 ConfigureAwait(false)을 사용하지 않는 한). 두 번째 스레드는 멀티 스레드입니다.

다중 스레드는 항상 프로그램에 추가 복잡성을 가져옵니다. 당신이 얻을 수있는 이익을 위해 그것을 교환하고자한다면 스스로에게 질문해야합니다.

+0

실제로 사용중인 라이브러리가'Task.Run'을 내부에 가지고 있는지 여부는 알 수 없습니다. "스레드가 없다"라는 주석 (질문 참조)에 설명 된 상황을 가질 수도 있고, 내부에'Task.Run'이있는 async 메소드를 제공하는 라이브러리를 가질 수도 있습니다. 후자의 경우, 외부에서 '대기'만 사용하는 경우에도 흐름은 멀티 스레드입니다. –

+0

@MassimilianoKraus 동의했지만, 그 점을 놓친 것 같습니다. 당신이 호출 한 어떤 라이브러리라도 멀티 스레드 코드를 숨길 수 있습니다. 필자가 작성하도록 선택한 코드로 멀티 스레딩을 도입하는 것에 대해 구체적으로 이야기하고 있습니다. – MEMark