2017-02-21 56 views
0

我有以下情形:如何使用異步/地等待着用,需要一個新的異步每個任務的多個任務場景/等待

客戶有許多賬戶,他們每個人都有一些附加卡。

現在我有一個請求,我需要在多個客戶的卡上查詢帳戶。我有異步方法分別查詢帳戶和卡,即FindAccounts(string customer),FindCards(string[] accounts)

所以我有這樣的方法:

public async Task<Data> FindCustomersWithCards(string[] customers) 
{ 
    var accountsTasks = customers.Select(_service.FindAccounts); 
    var accounts = await Task.WhenAll(accountsTasks); 

    var cardsTasks = accounts.Select(_service.FindCards); 
    var cards = await Tasks.WhenAll(cardsTasks)   
    ... 
} 

雖然這會工作有,你必須等待所有客戶的帳戶完成可以查詢卡之前的一個問題。一旦查詢特定客戶的賬戶完成(無需等待其他客戶),更高效的實施將繼續併爲客戶賬戶查詢卡。

我的問題是,如果可以這樣做async/await。我想我可以用ContinueWith來管理,但我不是100%確定可以將async/awaitContinueWith的方法混合使用。

+0

你是對的,ContinueWith聽起來像你想要的。你將爲每個賬戶建立一個更大的任務「獲取賬戶,然後獲取卡」 – Juan

+0

混合兩種方法怎麼樣?你知道有什麼缺點嗎? –

+0

是的,我的意思是把它混合起來,你可以在ContinueWith上等待,基本上你可以等待任何返回Task和ContinueWith的東西。如果你顯示你的方法簽名,我可以提供一個示例代碼 – Juan

回答

3

這可能是更明智的客戶和異步內的分裂它:

private async Task<Card> FindCardForCustomerAsync(string customer) 
{ 
    var account = await _service.FindAccountAsync(customer); 
    return await _service.FindCardAsync(account); 
} 

public async Task<Data> FindCustomersWithCards(string[] customers) 
{ 
    var cardsTasks = customers.Select(FindCardForCustomerAsync); 
    var cards = await Tasks.WhenAll(cardsTasks) 
    … 
} 

然而,這是值得考慮如何您FindAccountsFindCards工作效率的平衡。例如。如果它們作爲一個單獨的SELECT被推送到數據庫,那麼將其轉換爲多個較小工作位的更大併發性可能不值得更大量的開銷。可能出現的情況是,等待20個甚至200個結果的速度只比等待1個稍慢,然後,即使在考慮到所涉及的額外連接之前,分裂爲20個請求的增益也很少。

+0

謝謝,現在看起來相當明顯,但我沒有想到它自己出於某種原因。 @Panagiotis Kanavos在評論中指出了同樣的解決方案。我沒有一個SELECT的原因是我無法控制數據存儲並只允許這些分段查詢。如果我有機會,我寧願查詢它們:) –

+0

如果你的'FindAccounts'在幕後做了幾個調用,那麼可能性會更好,這會改善事情,或者至少不會讓它們變得更糟。 –

+0

'FindAccounts'確實只有一個電話,但問題是我必須先爲所有客戶找到賬戶,然後才能繼續使用他們的卡:) –

0

很難說如果tasks per customer方法能爲您帶來好處,找出它的最好方法就是對您的方案進行測試。

我做了一個簡單的事件(它看起來很雜亂),舉例說明如何修改第一種方法以允許每個客戶執行任務。
你已經提到你可以管理這個,我只是想在這裏發佈,所以任何對此感興趣的人都可以使用它。我使用Task.Run(...)來模擬異步任務。

public class Account 
{ 
    public string AccountName { get; set; } 
    public string CustomerName { get; set; } 
} 

public class Card 
{ 
    public string CardName { get; set; } 
    public string AccountName { get; set; } 
} 

public List<Account> Accounts { get; set; } 
public List<Card> Cards { get; set; } 

//OLD 
public async Task<string[]> FindAccounts(string customer) 
{ 
    return await Task.Run(() => 
    { 
     return Accounts.Where(a => a.CustomerName == customer).Select(a => a.AccountName).ToArray(); 
    }); 
} 

//OLD 
public async Task<string[]> FindCards(string[] accounts) 
{ 
    return await Task.Run(() => 
    { 
     return Cards.Where(c => accounts.Contains(c.AccountName)).Select(a => a.CardName).ToArray(); 
    }); 
} 

//NEW 
public async Task<string[]> FindCards(Task<string[]> findAccountsTasks) 
{ 
    return await Task.Run(async() => 
    { 
     var accounts = await findAccountsTasks; 
     return Cards.Where(c => accounts.Contains(c.AccountName)).Select(a => a.CardName).ToArray(); 
    }); 
} 

//NEW 
public async Task<string[]> FindCards(string customer) 
{ 
    return await await FindAccounts(customer).ContinueWith(FindCards); 
} 

private async void button7_Click(object sender, EventArgs e) 
{ 
    Accounts = new List<Account> 
    { 
     new Account {CustomerName = "Tomas", AccountName = "TomasAccount1"}, 
     new Account {CustomerName = "Tomas", AccountName = "TomasAccount2"}, 
     new Account {CustomerName = "Tomas", AccountName = "TomasAccount3"}, 
     new Account {CustomerName = "John", AccountName = "JohnAccount1"} 
    }; 

    Cards = new List<Card> 
    { 
     new Card {AccountName = "TomasAccount1", CardName = "TomasAccount1Card1"}, 
     new Card {AccountName = "TomasAccount1", CardName = "TomasAccount1Card2"}, 
     new Card {AccountName = "TomasAccount1", CardName = "TomasAccount1Card3"}, 
     new Card {AccountName = "TomasAccount1", CardName = "TomasAccount2Card1"}, 
     new Card {AccountName = "JohnAccount1", CardName = "JohnAccount1Card1"}, 
     new Card {AccountName = "JohnAccount1", CardName = "JohnAccount1Card2"}, 
    }; 

    var customers = new List<string> { "Tomas", "John" }.ToArray(); 

    //OLD 
    var accountstasks = customers.Select(FindAccounts); 
    var accounts = await Task.WhenAll(accountstasks); 

    var cardTasks = accounts.Select(FindCards); 
    var cards = await Task.WhenAll(cardTasks); 

    //NEW 
    cardTasks = customers.Select(FindCards); 
    cards = await Task.WhenAll(cardTasks); 
} 
+0

感謝您的建議,顯然有一個解決方案,更容易和更清晰:) –