Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Concurrent Collections and Unique elements

Îåêßíçóå áðü ôï ìÝëïò Bill . Τελευταία δημοσίευση από το μέλος Bill στις 05-10-2011, 20:50. Υπάρχουν 9 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  04-10-2011, 21:02 67626

    Concurrent Collections and Unique elements

    Έχω κάνει μια εφαρμογή η οποία παίζει με tasks . Έχω ένα BlockingCollection στο οποίο γεμίζω κάποια στοιχεία που θέλω , όλα καλά μέχρι εδώ . Ενώ η εφαρμογή μου παίζει πολύ καλά με τα tasks που έχω , διαπίστωσα πως στο BlockingCollection μου γεμίζει με ίδια στοιχεία. Θα ήθελα να μάθω αν υπάρχει τρόπος να το κάνω να μην βάζει διπλά στοιχεία ή αν υπάρχει τρόπος να πάρω τα Distinct και πως ? Επίσης υπάρχει άλλο Concurrent Collection που να γεμίζει με Distinct τιμές ? 
     Υ.Γ : Το πρόγραμμα μου δεν έχει  πρόβλημα με το να έχει το collection μου διπλά ..τριπλά..οκ στοιχεία απλά δεν θέλω να γεμίζει σκουπίδια.

    select DATEDIFF(dd,GetDate(),'20140731') AS EΠΟΜΕΝΕΣ_ΔΙΑΚΟΠΕΣ
  •  04-10-2011, 21:52 67627 σε απάντηση της 67626

    Απ: Concurrent Collections and Unique elements

    Δεν είναι και πολύ "Πρώτα Βήματα" η ερώτηση. Καταρχήν, θα πρέπει να ΜΗΝ βάζεις διπλά και τριπλά στο collection. Θα πρέπει να ελέγξεις τον κώδικα που προσθέτει τιμές για να δεις για ποιό λόγο συμβαίνει αυτό. Θα πρέπει να δώσεις ένα παράδειγμα του κώδικα που προσθέτει τις τιμές για να δούμε τί συμβαίνει. 

    Δεν είναι εγγυημένο ότι ένας έλεγχος του BlockingCollection θα σου δώσει σωστή απάντηση. Παρότι μπορείς άνετα να χρησιμοποιήσεις LINQ extensions όπως την Contains ή την FirstOrDefault για να κάνεις τον έλεγχο, μπορεί κάποιο άλλο thread να προσθέσει μία ίδια τιμή στο collection από τη στιγμή που έκανες τον έλεγχο μέχρι τη στιγμή που έκανες το Add. 

    Αλήθεια, τί προσπαθείς να κάνεις? Υπάρχουν διάφορες τεχνικές για να δουλέψεις με Task και ίσως κάποια να αντιμετωπίζει το πρόβλημα σου πολύ ευκολότερα από το να ψάχνεις για διπλές τιμές. Ίσως να μην χρειάζεται καν να χρησιμοποιήσεις το BlockingCollection και μία άλλη τεχνική να βολεύει περισσότερο.

    Όπως μου έλεγε και ο Μπιμπούδης, η σημαντικότερη εντολή στις "12 Εντολές του Synchronization" είναι Thou shalt live and die by coding conventions for synchronization. Ή με άλλα λόγια, πονάει να ανακαλύπτεις αυτά που έχουν ήδη βρει οι άλλοι

    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  04-10-2011, 22:19 67628 σε απάντηση της 67627

    Απ: Concurrent Collections and Unique elements

    Δεν βρήκα Parallel Programming να την βάλω Smile .
    Λοιπόν έχω φτιάξει έναν Parallel Web Crawler όπου κατεβάζει  web sites και τα αποθηκεύω στην βάση . Αλλά επειδή το collection μου έχει διπλά και τρίδιπλα μου "βρωμίζει" την βάση με σκουπίδια. Στην αρχή είχα απλό concurrentQueue  αλλά έβαλα BlockingCollection επειδή έχει και property Distinct (Το οποίο δεν δοκίμασα) .
     Συνοπτικά περιγράφω το πως δουλεύει :
    Με το που τρέξει το πρόγραμμα δημιουργώ n-1 tasks όπου n το πλήθος των λογικών πυρήνων (πχ για core i3 έχουμε 2 πυρήνες με 2 threads ανα πυρήνα έτσι είναι σαν να έχω 4 λογικούς πυρήνες) με το που δώσει ο χρήστης το site για crawl το πρόγραμμα αρχίζει και φορτώνει το collection μου με urls που τυγχόν αναφέρει το συγκεκριμένο site. Κάθε διαθέσιμο Task λαμβάνει ένα url και αρχίζει να το κατεβάζει αλλά και να προσθέτει στο BlockingCollection τα urls Που έχει και αυτο επαναλαμβάνεται στο άπειρο...μέχρι να το τερματίσω.  Δεν κάνω Sync μιας και δεν θέλω ,δεν έχει νόημα πιστεύω (κάνω λάθος ??) άλλωστε στόχος μου είναι να είναι ο crawler ταχύτερος απο έναν απλό. Κάθε φορά δημιουργώ n-1 tasks .
      Δεν θέλω να σας μπλέξω με το πως είναι στημένο απλά αν υπάρχει τρόπος να βάζω εξαρχής ή αργότερα Distinct urls θα με βοηθούσε.


    select DATEDIFF(dd,GetDate(),'20140731') AS EΠΟΜΕΝΕΣ_ΔΙΑΚΟΠΕΣ
  •  05-10-2011, 00:55 67629 σε απάντηση της 67628

    Απ: Concurrent Collections and Unique elements

    Μήπως θα ήταν καλύτερα να χρησιμοποιήσεις το ConcurrentDictionary αντί του BlockingCollection;

    Ακόμα κι ένας άνθρωπος μπορεί ν' αλλάξει τον κόσμο. Μη θέλεις να κυβερνήσεις. Απλά δείξε το μονοπάτι κι ο κόσμος θ' ακολουθήσει!!
  •  05-10-2011, 17:48 67635 σε απάντηση της 67628

    Απ: Concurrent Collections and Unique elements

    Κάνεις ένα βασικό λάθος όταν προσπαθείς εσύ να ορίσεις πόσα task θα τρέξουν. Ένα task ΔΕΝ αντιστοιχεί σε ένα thread. To TPL παίρνει τα task που του δίνεις και τα εκτελεί σε δικά του threads τα οποία τραβάει από δικό του thread pool. Είναι δουλειά του TPL να δημιουργήσει τόσα threads όσα μπορεί να εκμεταλλευτεί το μηχάνημα.

    Υποθέτω ότι αφού προσπαθείς να δημιουργήσεις συγκεκριμένο αριθμό tasks μάλλον τα χρησιμοποιείς σαν να είναι ξεχωριστά threads και προσπαθείς το καθένα να διαβάσει από ένα URL μέσα σε loop. Αυτό είναι το δεύτερο λάθος, καθώς ουσιαστικά αχρηστεύει τα tasks. Αντί για tasks που κάνουν ένα συγκεκριμένο πράγμα έχεις τώρα threads τα οποία προσπαθούν να κάνουν πολλά πράγματα μαζί. Λογικό είναι να είναι απαραίτητος αλλά και δύσκολος ο συγχρονισμός μεταξύ αυτών των threads. Ουσιαστικά με αυτό τον τρόπο έχεις αχρηστεύσει τα πλεονεκτήματα που σου δίνει η TPL.

     
    Μία καλύτερη λύση θα ήταν να βάλεις στό BlockingCollection όλα τα URL προς επεξεργασία και κάθε ένα που το επεξεργάζεσαι να το βάζεις σε ένα ConcurrentDictionary. Το dictionary θα πρέπει να το ελέγεις πριν προσθέσεις ένα νέο URL στο queue ή όταν τραβάς ένα για να το επεξεργαστείς. Κάθε φορά πο βρίσκεις ένα νέο URL κατά την επεξεργασία, θα το προσθέτεις και αυτό στο queue. Ο κώδικας θα είναι κάπως έτσι:

                var queue = new BlockingCollection<Uri>();
    
                var visitedUris=new ConcurrentDictionary<Uri, Uri>();
    
                Parallel.ForEach(queue.GetConsumingEnumerable(), 
                    uri =>
                        {
                            //Skip if visited
                            if (visitedUris.ContainsKey(uri)) 
                                return;
    
                            using (var client = new WebClient())
                            {
                                var pageContent = client.DownloadString(uri);
                                var discovered = Process(pageContent);
                                var newUris = from newUri in discovered
                                              where !visitedUris.ContainsKey(newUri)
                                              select newUri;
                                
                                foreach (var newUri in newUris)
                                {
                                    queue.Add(newUri);
                                }
                                //Don't care if it fails
                                visitedUris[uri] = uri;
                            }
                        });
    Με τον τρόπο αυτό αφήνεις το framework να δημιουργήσει όσα threads χρειάζεται αλλά μπορείς και να ελέγχεις εύκολα αν έχεις ήδη επεξεργασθεί μία διεύθυνση. 

    Εδώ τώρα μπορούν να γίνουν διάφορες τροποποιήσεις. Μπορείς να εκτελέσεις το download ασύγχρονα, εκμεταλλευόμενος την TaskFactory.FromAsync ή να χρησιμοποιήσεις την WebClient.DownloadStringTask από τα ParallelExtensionExtras για να κατεβάσεις τις σελίδες ασύγχρονα. Μετά μπορείς να κάνεις την επεξεργασία σε ένα άλλο task με την Task.ContinueWith. Ο κώδικας θα γίνει κάπως έτσι:

                Parallel.ForEach(queue.GetConsumingEnumerable(), 
                    uri =>
                        {
                            //Skip if visited
                            if (visitedUris.ContainsKey(uri)) 
                                return;
    
                            var client = new WebClient();
                            
                            var pageTask= client.DownloadStringTask(uri);
    
                            var processTask = pageTask.ContinueWith(p =>
                            {
                                client.Dispose();
    
                                var content = p.Result;
                                return Process(content);
                            });
                                
                            processTask.ContinueWith(t=>
                            {
                                var discovered=t.Result;
                                var newUris = from newUri in discovered
                                            where !visitedUris.ContainsKey(newUri)
                                            select newUri;
                                foreach (var newUri in newUris)
                                {
                                    queue.Add(newUri);
                                }
                                //Don't care if it fails
                                visitedUris[uri] = uri;
                            });                            
                            
                        });
    Το ωραίο σε όλη τη διαδικασία είναι ότι εσύ δεν χρειάζεται ποτέ να ορίσεις τον αριθμό των Task ή των Threads. Το αναλαμβάνει αυτό το framework. 

    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  05-10-2011, 19:38 67639 σε απάντηση της 67635

    Απ: Concurrent Collections and Unique elements

    Με έβαλες σε σκέψεις θα το δω αυτό που λες το να μην δίνω εξαρχής τον αριθμο των Tasks . Ευχαριστώ

    select DATEDIFF(dd,GetDate(),'20140731') AS EΠΟΜΕΝΕΣ_ΔΙΑΚΟΠΕΣ
  •  05-10-2011, 19:48 67640 σε απάντηση της 67639

    Απ: Concurrent Collections and Unique elements

    Αν σε ενδιαφέρει να δεις πως αντιστοιχίζονται tasks σε threads, θα πρέπει να διαβάσεις για τους Task Schedulers. Είναι οι κλάσεις οι οποίες αναλαμβάνουν να εκτελέσουν τα διάφορα tasks στα threads που έχουν διαθέσιμα στα δικά τους thread pools. Αν θέλεις, μπορείς να φτιάξεις το δικό σου Task Scheduler ο οποίος μπορεί να έχει κάποιο διαφορετικό pool ή να χρησιμοποιεί κάποιους δικούς σου κανόνες για να αποφασίσει τί θα τρέξει και πότε.

    Για παράδειγμα, στα ParallelExtensionExtras υπάρχει o QueuedTaskScheduler που καταλαβαίνει από priorities, o ThreadPerTaskScheduler που σηκώνει ένα thread για κάθε task (πλημύρρα!) και άλλοι περίεργοι τύποι.


    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  05-10-2011, 20:20 67641 σε απάντηση της 67640

    Απ: Concurrent Collections and Unique elements

    Παναγιώτης Καναβός:

    Αν σε ενδιαφέρει να δεις πως αντιστοιχίζονται tasks σε threads, θα πρέπει να διαβάσεις για τους Task Schedulers. Είναι οι κλάσεις οι οποίες αναλαμβάνουν να εκτελέσουν τα διάφορα tasks στα threads που έχουν διαθέσιμα στα δικά τους thread pools. Αν θέλεις, μπορείς να φτιάξεις το δικό σου Task Scheduler ο οποίος μπορεί να έχει κάποιο διαφορετικό pool ή να χρησιμοποιεί κάποιους δικούς σου κανόνες για να αποφασίσει τί θα τρέξει και πότε.

    Για παράδειγμα, στα ParallelExtensionExtras υπάρχει o QueuedTaskScheduler που καταλαβαίνει από priorities, o ThreadPerTaskScheduler που σηκώνει ένα thread για κάθε task (πλημύρρα!) και άλλοι περίεργοι τύποι.

    Σε πόσες γραμμές κώδικα θα μπορούσατε να υλοποιήσετε το Quartz.NET με τους παραπάνω schedulers; [;-)]

     

    George J.


    George J. Capnias: Χειροπρακτικός Υπολογιστών, Ύψιστος Γκουράρχης της Κουμπουτερολογίας
    w: capnias.org, t: @gcapnias, l: gr.linkedin.com/in/gcapnias
    dotNETZone.gr News
  •  05-10-2011, 20:47 67642 σε απάντηση της 67640

    Απ: Concurrent Collections and Unique elements

    Σε έναν σειριακό crawler / spider συνήθως βάζουν έναν timer ή μια while loop και με το tick πχ σε 2 sec ξανά τρέχει το method και κατεβάζει το επόμενο site. Εδώ εγώ δεν έχω βάλει κανένα timer και κάθε φορά που τελειώνουν τα task μου την δουλειά που κάνουν, ξανά τα δημιουργώ για αυτό και πάντα δίνω τον αριθμό των tasks που θέλω να έχω με την  προσέγγιση του n cores --> n-1 tasks . Θέλω να ρωτήσω το πώς θα κάνω τον crawler μου ασταμάτητο συνεχώς να τρέχει χωρίς να βάλω timer και να δημιουργώ εξ'αρχής τα tasks????

    select DATEDIFF(dd,GetDate(),'20140731') AS EΠΟΜΕΝΕΣ_ΔΙΑΚΟΠΕΣ
  •  05-10-2011, 20:50 67643 σε απάντηση της 67635

    Απ: Concurrent Collections and Unique elements

    Όπα το πιασα. Ξεχάστε την ερώτηση την τελευταία το βρήκα.Stick out tongue

    select DATEDIFF(dd,GetDate(),'20140731') AS EΠΟΜΕΝΕΣ_ΔΙΑΚΟΠΕΣ
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems