Tout d'abord: Ne pas utiliser DataTable
pour des opérations comme celles-ci:
- il est lent
- il y a consommer trop de mémoire
- et vous avez besoin d'attendre longtemps avant de pouvoir réellement commencer traitement des données
- Pendant ce temps, des cœurs supplémentaires ne font rien, puisque la lecture des données dans un
DataTable
n'est pas parralisée.
- En outre, lors de la lecture des données, le processeur est généralement presque sous-utilisé, car le réseau ou un autre retard d'E/S est souvent le facteur principal.
Encore une fois: Ne pas utiliser DataTable
pour des opérations comme celles-ci.
Utilisez plutôt le DataReader
. Cela vous permet de commencer immédiatement à consommer/traiter les données, au lieu d'attendre qu'elles soient chargées. La version la plus simple serait (exemple pour MS SQL Server):
var command = new SqlCommand()
{
CommandText = "SELECT * FROM Table";
Connection = new SqlConnection("InsertConnectionString");
};
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
var values = new object[reader.FieldCount];
reader.GetValues(values);
// process values of row
}
}
Le lecteur sera bloqué pendant l'exécution de votre code de traitement, ce qui signifie des lignes ne sont plus lues à partir du DB.
Si le code de traitement est lourd, cela peut valoir la peine d'utiliser la bibliothèque Task
pour créer des tâches qui effectuent la vérification, ce qui vous permettrait d'utiliser plusieurs cœurs. Cependant, il y a une surcharge de la création d'un Task
, si l'on Task
ne contient pas assez de « travail » que vous pouvez traiter en lots deux rangées ensemble:
public void ReadData()
{
var taskList = new List<Task<SomeResultType>>();
var command = new SqlCommand()
{
CommandText = "SELECT * FROM Table";
Connection = new SqlConnection("InsertConnectionString");
};
using(var reader = command.ExecuteReader())
{
var valueList = new List<object[]>(100);
while(reader.Read())
{
var values = new object[reader.FieldCount];
reader.GetValues(values);
valueList.Add(values);
if(valueList.Count == 100)
{
var localValueList = valueList.ToList();
valueList.Clear();
taskList.Add(Task<SomeResultType>.Factory.StartNew(() => Process(localValueList));
}
}
if(valueList.Count > 0)
taskList.Add(Task<SomeResultType>.Factory.StartNew(() => Process(valueList));
}
// this line completes when all tasks are done
Task.WaitAll(taskList.ToArray());
}
public SomeResultType Process(List<object[]> valueList)
{
foreach(var vals in valueList)
{
// put your processing code here, be sure to synchronize your actions properly
}
}
- La taille du lot (actuellement 100) dépend de la réelle le traitement est en cours et pourrait devoir être ajusté.
- Synchroniser tient ses propres défis, vous devez être très prudent sur les ressources partagées
Bonne explication Mais j'ai quelques questions: 1- ce qui serait mieux, la solution que vous aviez suggéré ou faisant plusieurs tâches, chacun d'eux utilise SQLReader pour lire une plage de lignes de la base de données (par ex.basé sur la pagination) en particulier dans le cas de traitement de résultat n'est pas très lourd. 2-Y at-il une différence significative de performance entre Task BackgroundWork? –