14

내 문제는 실제 프로젝트 문제를 기반으로하지만 난 System.Threading.Tasks 라이브러리를 사용하거나 스레드와 관련된 심각한 프로그래밍을 수행 한 적이 없어 내 질문에 특정 라이브러리에 대한 지식이 부족할 수 있습니다 프로그래밍 측면에서 비동기가 실제로 의미하는 바를 오해하고 있습니다.정말 비동기 두 SQL 쿼리를 만드는 방법

내 실제 사례가 있습니다. 사용자에 대한 데이터를 가져와야합니다. 현재 시나리오에서는 재무 데이터이므로 모든 사용자에게 Accounts, 모두 Deposits 및 모두 Consignations이 필요하다고 가정 해 보겠습니다. 내 경우에 이것은 각 속성에 대한 백만 건의 레코드를 쿼리하는 것을 의미하며 각 쿼리는 상대적으로 느려지므로 을 가져 오려면 Deposits을 가져 오는 것보다 몇 배 느립니다. 그래서 나는 내가 사용하는거야 세 은행 제품에 대한 세 가지 클래스를 정의하고 난 특정 사용자의 모든 은행 제품에 대한 데이터를 가져 오기 할 때이 같은 수행

List<Account> accounts = GetAccountsForClient(int clientId); 
List<Deposit> deposits = GetDepositsForClient(int clientId); 
List<Consignation> consignations = GetConsignationsForClient(int clientId); 

그래서 문제가 시작을 여기에 모든 세 가지 목록을 동시에 가져와야합니다. 왜냐하면 모든 사용자 데이터를 표시하는보기로 전달하기 때문입니다. 그래서 세 제품 모두에 대한 데이터를 수집하기위한 총 시간 (내가 제대로 여기 용어를 사용하고있는 경우)가 바로 지금 실행이 동기하지만입니다 :

Total_Time = Time_To_Get_Accounts + Time_To_Get_Deposits + Time_To_Get_Consignations 

이 좋지 않아 각 쿼리 때문에 상대적으로 느리기 때문에 총 시간이 꽤 길지만 accounts 쿼리는 다른 두 쿼리보다 훨씬 많은 시간이 걸리므로 오늘 내 머리 속에 들어간 아이디어는 "이 쿼리를 동시에 실행할 수 있다면 어떨까요?"입니다. 어쩌면 내 주제에 대한 나의 가장 큰 오해가 내게이 아이디어에 가장 가까운 것은 비동기 적이기 때문에 Total_Time은 가장 느린 쿼리 시간이 아니지만 세 쿼리의 합보다 훨씬 빠르다.

내 코드가 복잡하기 때문에 간단한 사용 사례를 만들었고, 내가 잘하려고 노력한 것을 반영했다.

public static async Task<int> GetAccounts() 
{ 
    int total1 = 0; 
    using (SqlConnection connection = new SqlConnection(connString)) 
    { 
     string query1 = "SELECT COUNT(*) FROM [MyDb].[dbo].[Accounts]"; 
     SqlCommand command = new SqlCommand(query1, connection); 
     connection.Open(); 
     for (int i = 0; i < 19000000; i++) 
     { 
      string s = i.ToString(); 
     } 
     total1 = (int) await command.ExecuteScalarAsync(); 
     Console.WriteLine(total1.ToString()); 
    } 
    return total1; 
} 

와 두 번째 방법 : 나는 두 가지 방법이이 같은 전화

public static async Task<int> GetDeposits() 
{ 
    int total2 = 0; 
    using (SqlConnection connection = new SqlConnection(connString)) 
    { 
     string query2 = "SELECT COUNT(*) FROM [MyDb].[dbo].[Deposits]"; 
     SqlCommand command = new SqlCommand(query2, connection); 
     connection.Open(); 
     total2 = (int) await command.ExecuteScalarAsync(); 
     Console.WriteLine(total2.ToString()); 
    } 
    return total2; 
} 

:

static void Main(string[] args) 
{ 
    Console.WriteLine(GetAccounts().Result.ToString()); 

    Console.WriteLine(GetDeposits().Result.ToString()); 
} 

당신은 내가 처음 GetAccounts()를 호출하고 나는 실행을 느리게 볼 수 있듯이 그래서 나는 다음 번 방법으로 계속할 수있는 기회를 준다. 그러나 나는 일정 시간 동안 어떤 결과도 얻지 못하고 있으며, 동시에 콘솔에 모두 인쇄됩니다.

그래서 문제 - 다음 방법으로 이동하기 위해 첫 번째 방법을 마칠 때까지 기다리지 않으려면 어떻게해야합니까? 일반적으로 코드 구조는 그리 중요하지 않습니다. 두 가지 쿼리가 동시에 실행될 수있는 방법이 있다면 알아 내고 싶습니다. 여기 샘플은 내가 원하는 결과를 얻을 수있는 지점까지 확장 될 수있는 나의 연구 결과입니다.

P.S 나는 그것을 사용하고있는 방법으로 시작했기 때문에 ExecuteScalarAsync();을 사용하고 있습니다. 실제로 나는 ScalarReader을 사용할 것입니다.

답변

12

아직 완료되지 않은 작업에 Result 속성을 사용하면 작업이 완료 될 때까지 호출 스레드가 차단됩니다.즉 GetDeposits 전화가 시작되기 전에 GetAccounts 작업을 완료해야한다는 것을 의미합니다.

이러한 방법이 동기식 CPU를 많이 사용하는 부분을 포함하여 병렬로 실행되도록하려면 해당 작업을 다른 스레드로 오프로드해야합니다. 이렇게하는 가장 간단한 방법은 Task.Run을 사용하는 것입니다 : 당신은 단순히이 메서드를 호출 동 기적으로는 사용 완료 될 때까지 기다릴 수 async 될 수 없습니다 그래서 await을 사용할 수 없습니다

static void Main(string[] args) 
{ 
    var accountTask = Task.Run(async() => Console.WriteLine(await GetAccounts())); 
    var depositsTask = Task.Run(async() => Console.WriteLine(await GetDeposits())); 

    Task.WhenAll(accountTask, depositsTask).Wait(); 
} 

Main 때문에 Wait.

+0

. 저의 원래 생각은 다른 순간에 결과를 얻는 것이 었습니다. 귀하의 설명에서 나는이 총 시간이 가장 느린 시간과 같을 것이라고 생각하지만 두 번째 메소드'for (int i = 0; i <19000000; i ++)를 추가했을 때 { string s = i.ToString(); }'시간이 두 배로 늘어났습니다. 이상합니다. 그것이 동시 적이었고이 경우 쿼리에 밀리 초가 걸리므로 메서드 중 하나만 느려지는 것과 같은 시간을 기다리고있었습니다. – Leron

+0

내가 스스로를 분명하게하는지 확실하지 않습니다. 내 생각에'GetAccounts()'가 3 초가 걸리고'GetDeposits()'가 초보다 작 으면 결과를 얻는데 3 초가 걸릴 것이라고 생각한다. 하지만 GetDeposits()를 3 초 동안 실행하지 않고 3 초 동안 실행하도록하면, 가장 느린 시간이고 작업이 동시에 실행되므로 6 초 후에 결과를 얻습니다. 이 접근법을 그대로 사용하면 일어나는 일이 거의 발생합니다. 내가 놓친 게 있니? – Leron

+0

@Leron 비동기는 병렬과 동일하지 않습니다. 메소드에는 비동기 파트와 동기 파트 (I/O 및 CPU)가 있습니다. 비동기 파트를 내 대답과 마찬가지로 동시에 실행할 수 있습니다. 동기 부분을 병렬로 실행하려면 'Task.Run'과 함께 2 개의 쓰레드를 사용해야합니다. 너가 원하는게 그거야? – i3arnon

2

나는 일반적으로 Task.WaitAll을 사용하는 것을 선호합니다. 이 코드 세그먼트에 대한 설정하려면, 난 그냥 INT를 반환 할 GetAccounts/GetDeposits 서명을 변경 (public static int GetAccounts())

내가 유효성을 검사하는 수익을 할당과 같은 스레드에서 Console.WriteLine을 배치하는 GetAccounts 전에 한 GetDeposits 반환 가지고 있지만,

Task<int> accountTask = GetAccounts(); 
Task<int> depositsTask = GetDeposits(); 

int[] results = await Task.WhenAll(accountTask, depositsTask); 

int accounts = results[0]; 
int deposits = results[1]; 
+0

정확히 내가 무엇을 찾고 있었습니까. 방금 한 일에 대해 더 많은 정보를 얻는 방법에 대한 지침을 제게 제공 할 수 있습니까? – Leron

+0

'Task.WaitAll()'은 리턴 타입이'void'이므로 태스크가 뭔가를 리턴하면 결과를 얻지 못할 것입니다. 그것을 얻으려면'await Task.WhenAll()'을 사용할 필요가 있습니다. – abatishchev

+0

이것은 비동기 적이 아닙니다. 병렬로 실행되는 2 개의 완전 동기식 조작입니다. – i3arnon

6

후 이동하는 비동기 병렬로 두 가지 작업을 수행 할 수있는 방법 아마 가장 불필요한 것입니다 e AJAX는 페이지가 렌더링 된 후 페치하고 데이터를 저장소에 저장합니다. 각 AJAX 가져 오기는 비동기입니다. 서버에서 동시 SQL 쿼리를 생성하려면?

사용법 :

// Add some queries ie. ThreadedQuery.NamedQuery([Name], [SQL]) 
var namedQueries= new ThreadedQuery.NamedQuery[]{ ... }; 

System.Data.DataSet ds = ThreadedQuery.RunThreadedQuery(
"Server=foo;Database=bar;Trusted_Connection=True;", 
namedQueries).Result; 


string msg = string.Empty; 
foreach (System.Data.DataTable tt in ds.Tables) 
msg += string.Format("{0}: {1}\r\n", tt.TableName, tt.Rows.Count); 

출처 : 내가 뭔가를하지 않는 생각 I3arnon @

public class ThreadedQuery 
{ 

    public class NamedQuery 
    { 
     public NamedQuery(string TableName, string SQL) 
     { 
      this.TableName = TableName; 
      this.SQL = SQL; 
     } 
     public string TableName { get; set; } 
     public string SQL { get; set; } 
    } 
    public static async System.Threading.Tasks.Task<System.Data.DataSet> RunThreadedQuery(string ConnectionString, params NamedQuery[] queries) 
    { 

     System.Data.DataSet dss = new System.Data.DataSet(); 
     List<System.Threading.Tasks.Task<System.Data.DataTable>> asyncQryList = new List<System.Threading.Tasks.Task<System.Data.DataTable>>(); 

     foreach (var qq in queries) 
      asyncQryList.Add(fetchDataTable(qq, ConnectionString)); 

     foreach (var tsk in asyncQryList) 
     { 
      System.Data.DataTable tmp = await tsk.ConfigureAwait(false); 
      dss.Tables.Add(tmp); 
     } 

     return dss; 

    } 

    private static async System.Threading.Tasks.Task<System.Data.DataTable> fetchDataTable(NamedQuery qry, string ConnectionString) 
    { 
     // Create a connection, open it and create a command on the connection 
     try 
     { 

      System.Data.DataTable dt = new System.Data.DataTable(qry.TableName); 
      using (SqlConnection connection = new SqlConnection(ConnectionString)) 
      { 
       await connection.OpenAsync().ConfigureAwait(false); 
       System.Diagnostics.Debug.WriteLine("Connection Opened ... " + qry.TableName); 
       using (SqlCommand command = new SqlCommand(qry.SQL, connection)) 
       { 
        using (SqlDataReader reader = command.ExecuteReader()) 
        { 
         System.Diagnostics.Debug.WriteLine("Query Executed ... " + qry.TableName); 

         dt.Load(reader); 

         System.Diagnostics.Debug.WriteLine(string.Format("Record Count '{0}' ... {1}", dt.Rows.Count, qry.TableName)); 

         return dt; 
        } 
       } 
      } 
     } 
     catch(Exception ex) 
     { 

      System.Diagnostics.Debug.WriteLine("Exception Raised ... " + qry.TableName); 
      System.Diagnostics.Debug.WriteLine(ex.Message); 

      return new System.Data.DataTable(qry.TableName); 
     } 

    } 
} 
+0

@ Leron : [전체 예제가 있습니다.] (https://dotnetfiddle.net/IsAJwV) – abatishchev

0

그것은 ASP.NET 우리 인 경우 :이 여기에 Task.WaitAll

 private static void Main(string[] args) { 

     int getAccountsTask = 0; 
     int getDepositsTask = 0; 
     List<Task> tasks = new List<Task>() { 
      Task.Factory.StartNew(() => { 
       getAccountsTask = GetAccounts(); 
       Console.WriteLine(getAccountsTask); 
      }), 
      Task.Factory.StartNew(() => { 
       getDepositsTask = GetDeposits(); 
       Console.WriteLine(getDepositsTask); 

      }) 

     }; 
     Task.WaitAll(tasks.ToArray()); 



    } 
관련 문제