2016-12-13 2 views
-2

J'essaie de combiner ma déclaration de PLINQ comme ceci:La combinaison PLINQ avec la méthode Async

Enumerable.Range(0, _sortedList.Count()).AsParallel().WithDegreeOfParallelism(10) 
      .Select(i => GetTransactionDetails(_sortedList[i].TransactionID)) 
      .ToList(); 

Avec une méthode async comme ceci:

private async void GetTransactionDetails(string _trID) 
{ 
     await Task.Run(() => 
     { 
     }); 
} 

Alors que je peux simplement ajouter un opérateur await ici:

Enumerable.Range(0, _sortedList.Count()).AsParallel().WithDegreeOfParallelism(10) 
      .Select(i => await GetTransactionDetails(_sortedList[i].TransactionID)) 
      .ToList(); 

Comment puis-je y parvenir?

P.S. De cette façon, je pouvais faire 5-10 requêtes HTTP simultanément tout en veillant à ce que l'utilisateur final ne se sent pas de gel « écran » en le faisant ...

+0

Pourquoi essayez-vous de paralléliser le * démarrage * d'une opération asynchrone, étant donné que le démarrage va prendre à peu près zéro temps du tout. PLINQ est destiné aux opérations liées à l'UC exécutées depuis longtemps; ce n'est pas ce que tu as. De plus, il semble que 'GetTransactionDetails' utilise l'anti-pattern async over sync. Il ne devrait pas décharger le travail sur un autre thread, il devrait plutôt être une méthode synchrone. Si l'appelant veut l'appeler avec Task.Run, ils peuvent, s'ils veulent faire autre chose, comme utiliser PLINQ, alors ils peuvent le faire. – Servy

+0

@Servy pourriez-vous montrer comment je pourrais mettre en œuvre ce que vous venez de dire sur un exemple plus pratique? – User987

+0

Supprimez 'Task.Run' de' GetTransactionDetails', et faites-le faire de façon synchrone, permettant ainsi à PLINQ de le paralléliser. – Servy

Répondre

2

Il y a deux approches que vous pouvez prendre.

D'abord, l'approche « plus correcte » (qui est aussi plus de travail). Faire GetTransactionDetails dans une méthode async appropriée (à savoir, ne pas utiliser Task.Run):

private async Task GetTransactionDetailsAsync(string _trID); 

Ensuite, vous pouvez appeler cette méthode simultanément:

var tasks = _sortedList.Select(x => GetTransactionDetailsAsync(x.TransactionID)); 
await Task.WhenAll(tasks); 

Si vous devez limiter la concurrence, utilisez un SemaphoreSlim.

La seconde approche est plus inutile (en termes d'utilisation de fil), mais il est probablement plus facile étant donné que les parties de votre code que nous avons vu. La seconde approche est de laisser les E/S tout synchrone et faites-le de façon régulière PLINQ:

private void GetTransactionDetails(string _trID); 

_sortedList.AsParallel().WithDegreeOfParallelism(10).Select(x => GetTransactionDetails(x.TransactionID)).ToList(); 

Pour éviter de bloquer le thread d'interface utilisateur, vous pouvez envelopper cela dans un seul Task.Run:

await Task.Run(() => _sortedList.AsParallel().WithDegreeOfParallelism(10).Select(x => GetTransactionDetails(x.TransactionID)).ToList()); 
+0

Clairement incroyable réponse, merci! L'attente Task.Run() est exactement ce dont j'avais besoin dans ce cas ... Bien que je devais utiliser Parallel.For avec la combinaison de Parallel.For loop au lieu de celui que vous avez montré dans l'exemple parce que la méthode .Select doesn ' t travailler avec des méthodes void, au moins c'est ce que le compilateur m'a montré? – User987

+0

J'ai des méthodes asynchrones liées à la CPU qui font un petit peu d'E/S mais doivent être parallélisées pour les choses liées au CPU. Les interfaces qu'il utilise n'ont pas d'API synchrone. Ce que j'ai fait était 'Task.WhenAll (items.Select (item => Task.Run (async() => {/ * méthode qui attend * /})))'. Le Task.Run est de gérer le travail lié au CPU qui se produit avant le premier attendre. Queue tout à la fois à la piscine de thread semble être le plus rapide jusqu'à présent. – jnm2

+0

Quoi qu'il en soit, je ne pense pas que cela gaspille jamais un thread sur IO. – jnm2