2010-08-16 6 views
1

J'ai une application avec laquelle j'ai déjà commencé à travailler et il me semble que j'ai besoin de repenser un peu les choses. L'application est une application winform pour le moment. Quoi qu'il en soit, je permets à l'utilisateur d'entrer le nombre de threads qu'ils aimeraient avoir en cours d'exécution. J'autorise également l'utilisateur à allouer le nombre d'enregistrements à traiter par thread. Ce que j'ai fait est de faire une boucle sur le nombre de threads variables et de créer les threads en conséquence. Je n'effectue aucun verrouillage (et je ne suis pas sûr d'en avoir besoin ou pas) sur les threads. Je suis novice en matière de threading et je suis confronté à un problème possible avec plusieurs cœurs. J'ai besoin de conseils sur la façon dont je peux améliorer cette performance.Aide .NET multithread

Avant de créer un thread, certains enregistrements sont extraits de ma base de données pour être traités. Cet objet liste est envoyé au thread et bouclé. Une fois qu'il atteint la fin de la boucle, le thread appelle les fonctions de données pour récupérer de nouveaux enregistrements, en remplaçant les anciens dans la liste. Cela continue jusqu'à ce qu'il n'y ait plus d'enregistrements. Voici mon code:

private void CreateThreads() 
{ 
    _startTime = DateTime.Now; 
    var totalThreads = 0; 
    var totalRecords = 0; 
    progressThreadsCreated.Maximum = _threadCount; 
    progressThreadsCreated.Step = 1; 
    LabelThreadsCreated.Text = "0/" + _threadCount.ToString(); 
    this.Update(); 
    for(var i = 1; i <= _threadCount; i++) 
    { 
     LabelThreadsCreated.Text = i + "/" + _threadCount; 
     progressThreadsCreated.Value = i; 
     var adapter = new Dystopia.DataAdapter(); 
     var records = adapter.FindAllWithLocking(_recordsPerThread,_validationId,_validationDateTime); 
     if(records != null && records.Count > 0) 
     { 
      totalThreads += 1; 
      LabelTotalProcesses.Text = "Total Processes Created: " + totalThreads.ToString(); 




      var paramss = new ArrayList { i, records }; 
      var thread = new Thread(new ParameterizedThreadStart(ThreadWorker)); 
      thread.Start(paramss); 
     } 

     this.Update(); 
    } 


} 


private void ThreadWorker(object paramList) 
{ 
    try 
    { 
     var parms = (ArrayList) paramList; 
     var stopThread = false; 
     var threadCount = (int) parms[0]; 
     var records = (List<Candidates>) parms[1]; 
     var runOnce = false; 
     var adapter = new Dystopia.DataAdapter(); 
     var lastCount = records.Count; 
     var runningCount = 0; 
     while (_stopThreads == false) 
     { 
      if (!runOnce) 
      { 
       CreateProgressArea(threadCount, records.Count); 
      } 
      else 
      { 
       ResetProgressBarMethod(threadCount, records.Count); 
      } 


      runOnce = true; 
      var counter = 0; 
      if (records.Count > 0) 
      { 
       foreach (var record in records) 
       { 
        counter += 1; 
        runningCount += 1; 
        _totalRecords += 1; 
        var rec = record; 
        var proc = new ProcRecords(); 
        proc.Validate(ref rec); 

        adapter.Update(rec); 

        UpdateProgressBarMethod(threadCount, counter, emails.Count, runningCount); 

        if (_stopThreads) 
        { 
         break; 
        } 
       } 

       UpdateProgressBarMethod(threadCount, -1, lastCount, runningCount); 

       if (!_noRecordsInPool) 
       { 
        records = adapter.FindAllWithLocking(_recordsPerThread, _validationId, _validationDateTime); 
        if (records == null || records.Count <= 0) 
        { 
         _noRecordsInPool = true; 
         break; 
        } 
        else 
        { 
         lastCount = records.Count; 
        } 
       } 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.Message); 
    } 
} 
+0

Quels sont les problèmes que vous rencontrez? En outre, je suis assez sûr que vous devez augmenter le thread d'interface utilisateur dans votre exception capture MessageBox.Show. Je suppose que si votre barre de progression fonctionne, vous le faites déjà, mais voici une question de référence de l'idée (http://stackoverflow.com/questions/2367718/c-automating-the-invokerequired-code-pattern). Peut-être que vous perdez vos exceptions à cause de cela? –

+0

J'utilise des délégués pour mettre à jour l'interface utilisateur. Cela fonctionne bien. Merci pour les commentaires, cependant. – DDiVita

Répondre

3

Je permet à l'utilisateur d'entrer le nombre de threads qu'ils aimeraient avoir cours d'exécution. Je permet également à l'utilisateur de affecter le nombre d'enregistrements à processus par thread.

Ce n'est pas quelque chose que vous voulez vraiment exposer à l'utilisateur. Qu'est-ce qu'ils sont censés mettre? Comment peuvent-ils déterminer ce qui est le meilleur? C'est un détail d'implémentation qui vous convient le mieux, ou mieux, le CLR ou une autre bibliothèque.

Je ne fonctionne pas tout blocage (et pas sûr que je dois ou non) sur les threads.

La majorité des problèmes que vous aurez avec le multithreading proviendra de l'état partagé. Plus précisément, dans votre méthode ThreadWorker, il semble que vous vous référez aux données partagées suivantes: _stopThreads, _totalRecords, _noRecordsInPool, _recordsPerThread, _validationId et _validationDateTime. Cependant, ce n'est pas parce que ces données sont partagées que vous aurez des problèmes. Tout dépend de qui les lit et les écrit. Par exemple, je pense que _recordsPerThread est seulement écrit une fois initialement, puis lu par tous les threads, ce qui est bien. _totalRecords, cependant, est à la fois lu et écrit par chaque thread.Vous pouvez rencontrer des problèmes de threads ici car _totalRecords += 1; consiste en une lecture-écriture non-atomique. En d'autres termes, deux threads peuvent lire la valeur _totalRecords (disons qu'ils lisent tous les deux la valeur 5), puis incrémenter leur copie, puis les réécrire. Ils vont tous les deux réécrire la valeur 6, qui est maintenant incorrecte puisqu'elle devrait être de 7. C'est un classique race condition. Pour ce cas particulier, vous pouvez utiliser Interlocked.Increment pour mettre à jour le champ de manière atomique.

En général, pour effectuer la synchronisation entre threads en C#, vous pouvez utiliser les classes dans l'espace de noms System.Threading, par ex. Mutex, Semaphore, et probablement le plus commun, Monitor (équivalent à lock) qui permet à un seul thread d'exécuter une partie spécifique du code à la fois. Le mécanisme que vous utilisez pour synchroniser dépend entièrement de vos exigences de performance. Par exemple, si vous lancez un lock autour du corps de votre ThreadWorker, vous annulez les gains de performance obtenus grâce au multithreading en sérialisant efficacement le travail. Coffre-fort, mais lent :(Par contre, si vous utilisez Interlocked.Increment et judicieusement ajouter d'autres synchronisation si nécessaire, vous maintiendrez vos performances et votre application sera correcte :)

Une fois que vous avez obtenu votre travailleur méthode pour être thread-safe, vous devez utiliser un autre mécanisme pour gérer vos threads. ThreadPool a été mentionné, et vous pouvez également utiliser le Task Parallel Library, qui résume le ThreadPool et détermine intelligemment et met à l'échelle le nombre de threads à utiliser. De cette façon, vous prenez le fardeau de l'utilisateur pour déterminer quel nombre magique de threads ils devraient exécuter.

0

La réponse évidente est de se demander pourquoi vous voulez des threads en premier lieu? Où est l'analyse et les benchmarks qui montrent que l'utilisation de threads sera un avantage? Comment assurez-vous que les threads non-GUI n'interagissent pas avec le GUI? Comment vous assurez-vous que deux threads n'interagissent pas de la même manière avec les mêmes variables ou structures de données? Même si vous réalisez que vous avez besoin d'utiliser le verrouillage, comment vous assurez-vous que les verrous n'entraînent pas pour chaque thread de traiter en série leur charge de travail, en supprimant les avantages que plusieurs threads ont pu fournir?

+0

J'ai besoin de plusieurs threads parce que nous validons des enregistrements et faisons des connexions netwrok pour valider ces données. Nous traitons des millions d'enregistrements dans nos bases de données et n'avoir qu'un seul processus à faire ne semble pas pratique. Nous avons essayé d'utiliser un seul thread, mais c'était beaucoup trop lent d'un processus. – DDiVita

+1

Sonne extrêmement lié à moi-même. (Faire des connexions pour valider les données?) Je reste suspect que les threads vont réellement gagner quelque chose, à partir de cette seule description. – Arafangion

+0

Dans cette situation est fait. Merci – DDiVita

4

Quelque chose de simple que vous pourriez faire pour améliorer perf serait d'utiliser un ThreadPool pour gérer votre création de threads. Cela permet au système d'exploitation d'allouer un groupe de threads payant la pénalité de création de thread une fois au lieu de plusieurs fois.

Si vous décidez de passer à .NET 4.0, Task s serait une autre façon de procéder.

+0

Salut, bon de vous voir ici :) –

+0

Ce n'est pas la réponse à la question - la question n'est pas de savoir comment gérer un pool de threads ou d'optimiser le nombre de threads - il s'agit de la question beaucoup plus fondamentale de comment utiliser les discussions en premier lieu. Les utilisateurs en apprennent plus sur les pools de threads APRÈS avoir appris le verrouillage et d'autres aspects fondamentaux de plusieurs conceptions à filetages. (C'est un outil, en d'autres termes). – Arafangion

+0

En fait, j'ai répondu à sa question. Très précisément. Il a demandé "... comment je peux rendre cela plus performant." Bien que vous ayez raison sur le fait qu'une compréhension plus approfondie de Threads est importante, ThreadPool la rendra plus performante. – linuxuser27