sonyxp Δημοσ. 7 Απριλίου 2014 Δημοσ. 7 Απριλίου 2014 Καλημέρα ομορφόπαιδα! Δεν έχω ασχοληθεί με το κομμάτι των Threads, θα ήθελα μια μικρή βοήθεια για κάποια πράγματα Πως να τρέξω ένα κομμάτι κώδικα το οποίο δεν δεσμεύει το υπόλοιπο πρόγραμμα, θέλω πχ όταν συμβεί ένα γεγονός να εκτυπώσω ένα μήνυμα για 3 δευτερόλεπτα. (Δηλαδή να εκτυπώσω ένα μήνυμα και να ξεκινήσω μια διαδικασία που θα περιμένει 3 δευτερόλεπτα πριν σβήσει το μήνυμα)(Timer and not Thread?) Πως μπορώ να κάνω Sleep(...) το New thread και όχι το Main Thread (ο κάτω κώδικας είναι λάθος) //.... // Reward! if (oldCounter >= 4) { lpPoints += 100; lblScore.Content = lpPoints.ToString(); lblMessage.Content = "Whatever!"; Thread clsThread = new Thread(new ThreadStart(ClearMessage)); clsThread.Start(); } } public void ClearMessage() { Thread.Sleep(3000); // This stack the main thread! :/ lblMessage.Content = ""; } //...
Papakaliati Δημοσ. 7 Απριλίου 2014 Δημοσ. 7 Απριλίου 2014 Γενικα μην χρησιμοποιεις ποτε sleep . Ειναι κακια συνηθεια που μπορει να σου δημιουργησει προβληματα στην εφαρμογη. Χρησιμοποιησε timer.
defacer Δημοσ. 7 Απριλίου 2014 Δημοσ. 7 Απριλίου 2014 Πως να τρέξω ένα κομμάτι κώδικα το οποίο δεν δεσμεύει το υπόλοιπο πρόγραμμα, θέλω πχ όταν συμβεί ένα γεγονός να εκτυπώσω ένα μήνυμα για 3 δευτερόλεπτα. (Δηλαδή να εκτυπώσω ένα μήνυμα και να ξεκινήσω μια διαδικασία που θα περιμένει 3 δευτερόλεπτα πριν σβήσει το μήνυμα)(Timer and not Thread?) Πως μπορώ να κάνω Sleep(...) το New thread και όχι το Main Thread (ο κάτω κώδικας είναι λάθος) Είναι overkill να χρησιμοποιήσεις καινούριο thread και το "σωστό" είναι όντως timer, αλλά στη συγκεκριμένη περίπτωση η διαφορά θα είναι καθαρά ακαδημαϊκή. Ο κώδικας φαίνεται σωστός εκτός του ότι όπως λέει το παπί από το νεο thread που δεν είναι ο owner του label πρέπει να κάνεις BeginInvoke. Γενικα μην χρησιμοποιεις ποτε sleep . Ειναι κακια συνηθεια που μπορει να σου δημιουργησει προβληματα στην εφαρμογη. Χρησιμοποιησε timer. Γιατί; Τι εννοείς "μπορεί να δημιουργήσει προβλήματα"; Και η εντολή delete μπορεί να δημιουργήσει προβλήματα αν τη χρησιμοποιήσεις λάθος, αλλά αυτός δεν είναι ικανοποιητικός λόγος για να την ξεχάσουμε.
Papakaliati Δημοσ. 7 Απριλίου 2014 Δημοσ. 7 Απριλίου 2014 Γιατί; Τι εννοείς "μπορεί να δημιουργήσει προβλήματα"; Και η εντολή delete μπορεί να δημιουργήσει προβλήματα αν τη χρησιμοποιήσεις λάθος, αλλά αυτός δεν είναι ικανοποιητικός λόγος για να την ξεχάσουμε. Επειδη το timer κανει ακριβως την ιδια δουλεια χωρις να χρειαζεται να παγωνει το thread. Eφοσον λοιπον μπορεις να αποφυγεις το sleep τοσο ευκολα γιατι να μην το κανεις ?
defacer Δημοσ. 7 Απριλίου 2014 Δημοσ. 7 Απριλίου 2014 Γενικα μην χρησιμοποιεις ποτε sleep . To point μου είναι ότι εκτός κι αν λέγοντας "ποτέ" εννοείς "ποτέ όταν κάνεις αυτό το συγκεκριμένο πράγμα που κάνεις εδώ", η δήλωση αυτή τα πιάνει όλα σβάρνα και καταλήγει σε λάθος συμπέρασμα. Γενικά μιλώντας ένα thread που δεν έχει άλλη δουλειά να κάνει και που δεν είναι runnable (π.χ. επειδή είναι σε Sleep) δεν ενοχλεί κανέναν.
Papakaliati Δημοσ. 7 Απριλίου 2014 Δημοσ. 7 Απριλίου 2014 To point μου είναι ότι εκτός κι αν λέγοντας "ποτέ" εννοείς "ποτέ όταν κάνεις αυτό το συγκεκριμένο πράγμα που κάνεις εδώ", η δήλωση αυτή τα πιάνει όλα σβάρνα και καταλήγει σε λάθος συμπέρασμα. Γενικά μιλώντας ένα thread που δεν έχει άλλη δουλειά να κάνει και που δεν είναι runnable (π.χ. επειδή είναι σε Sleep) δεν ενοχλεί κανέναν. Να στο θεσω αλλιως. Το sleep μπορει να το χρησιμοποιησεις λαθος οπως ανεφερες αμα ειναι runnable. Το timer ποτε. Στο μελλον ισως καποιος πειραξει το thread που κανεις το sleep και να το κανει runnable χωρις να προσεξει οτι εσυ χρησιμοποιεις sleep, επειδη προφανος θα θεωρει οτι δεν παιζει να χρησιμοποιησει καποιος και θα χασει χ ωρα μεχρι να δει γιατι κατι δεν παει καλα. Η και να το δει οτι εχεις χρησιμοποιησει το sleep, θα αναγκαστει να το κανει refactor, αρα και παλι καταληγουμε στο οτι μιλαμε για κακο κωδικα και κακια συνηθεια. Για αυτον τον λογο συνεχιζω να ειμαι απολυτος και να λεω, ποτε μην χρησιμοποιεις sleep.
sonyxp Δημοσ. 7 Απριλίου 2014 Μέλος Δημοσ. 7 Απριλίου 2014 Θα δώ λίγο τα Timers, απλά επειδή κάνω εφαρμογή-παιχνίδι που έχει πολλά στοιχεία-χρόνους ... σκέφτηκα "μην μπλέξω τα μπούτια με Timers αν βάλω drag&drop και τα διαχειριστώ μέσω κώδικα... βασικά θα κάνω και την δήλωση μέσω κώδικα ..." Κάποια βοήθεια για το Invoke; τι ρόλο βαράει; (όχι κώδικα) ;
defacer Δημοσ. 7 Απριλίου 2014 Δημοσ. 7 Απριλίου 2014 Να στο θεσω αλλιως. Το sleep μπορει να το χρησιμοποιησεις λαθος οπως ανεφερες αμα ειναι runnable. Το timer ποτε. Famous last words. Επίσης, άλλη δουλειά κάνει το ένα κι άλλη το άλλο. Εσύ λες πως το δίκαννο μπορεί να το χρησιμοποιήσεις λάθος για να σπάσεις το λουκέτο ενώ το σφυρί ποτέ (που δεν ισχύει κιόλας), άρα δίκαννο δε χρησιμοποιούμε ποτέ και για κανένα λόγο. Και για κυνήγι με το σφυρί θα πάμε; Δε γίνεται συζήτηση έτσι, το καταλαβαίνεις φαντάζομαι. Κάποια βοήθεια για το Invoke; τι ρόλο βαράει; (όχι κώδικα) ; Κάθε control είναι "owned" από το thread στο οποίο δημιουργήθηκε. Tυπικά αυτό το thread τρέχει ένα endless loop που συχνά το λέμε message pump ή ότι το thread κάνει pump messages etc -- αυτό δεν έχει να κάνει με WPF συγκεκριμένα, είναι χαρακτηριστικό του DNA των Windows (και επίσης είναι ο λόγος που δεν κάνεις Sleep κλπ σ' αυτό το thread γιατί όσο είναι σε sleep δεν κάνει και pump). Δε μπορείς να ακουμπήσεις control από άλλο thread εκτός αυτού που το δημιούργησε. Όταν χρειάζεται να κάνεις κάτι τέτοιο χρησιμοποιείς Invoke, στην οποία περνάς ένα delegate και αυτή ουσιαστικά "πακετάρει" το delegate μέσα σε ένα window message και το στέλνει στο thread-παραλήπτη. Αυτό το thread υποτίθεται πως θα τρέχει message pump, οπότε όταν πάρει το μήνυμα θα δει "α πρέπει να τρέξω αυτόν τον delegate" και θα τον τρέξει, οπότε έτσι "μεταφέρεις" τη δουλειά που πρέπει στο UI thread. H Invoke() περιμένει να γίνει pump το συγκεκριμένο μήνυμα και να τελειώσει εκείνο το iteration του message loop, ενώ η BeginInvoke() απλά το προγραμματίζει να γίνει και προχωρά επιτόπου. 2
albNik Δημοσ. 8 Απριλίου 2014 Δημοσ. 8 Απριλίου 2014 Το παρακατω θα εμφανισει τη λεξη "whatever" για 3 δευτερολεπτα (το UI θα ειναι responsive). void ShowDelay(int ms) { label1.Text = "Whatewer"; var timer = new Timer() { Interval = ms }; timer.Start(); timer.Tick += (o, e) => { label1.Text = ""; timer.Dispose(); }; } private void button1_Click(object sender, EventArgs e) { ShowDelay(3000); } Το Sleep και γενικοτερα τα blocked threads (WhaitSleepJoin) σπαταλουν resources. Οταν ενα thread γινεται blocked, το ThreadPool προσπαθει να διαθεσει αλλο ενα επιπλέον thread για να ικανοποιήσει την εφαρμογή Κάθε thread θελει περιπου 1ΜΒ μνημη, οσο πιο πολλα threads τοσο περισσότερα context swiches. Αν εχεις μια εφαρμογη chat και δεν κανεις καλη χρηση των threads π.χ. διαθετεις 1 thread per user ο οποιος 99% του χρονου ειναι ανενεργος (δλδ το thread blocked) δεν μπορεις να εξυπηρετησεις πανω απο 20-25 ταυτοχρονα. Ενας γενικός κάνονας ειναι ότι αν κάτι θα αργήσει πανω απο 50ms δεν πρέπει να μπλοκάρεις (WhaitSleepJoin) περιμενοντας το, αστο να επιστρεψει (με callback) οταν τελειώσει.
defacer Δημοσ. 8 Απριλίου 2014 Δημοσ. 8 Απριλίου 2014 Το παρακατω θα εμφανισει τη λεξη "whatever" για 3 δευτερολεπτα (το UI θα ειναι responsive). void ShowDelay(int ms) { label1.Text = "Whatewer"; var timer = new Timer() { Interval = ms }; timer.Start(); timer.Tick += (o, e) => { label1.Text = ""; timer.Dispose(); }; } private void button1_Click(object sender, EventArgs e) { ShowDelay(3000); } Αυτό είναι η καλύτερη λύση για την περίπτωση αλλά όπως φαίνεται χρησιμοποιείς windows forms timer ενώ η εφαρμογή μου φαίνεται για WPF -- δεν είμαι σίγουρος αν θα υπάρξει κάποιο θέμα, αλλά γι' αυτή την περίπτωση υπάρχει ο DispatcherTimer που βασικά δουλεύει με τον ίδιο τρόπο. Επίσης είναι πολύ σημαντικό το ότι ο winforms Timer και ο DispatcherTimer είναι ειδικά σχεδιασμένοι και λειτουργούν έτσι ώστε να μη χρειάζεται να κάνεις Invoke στο callback σου -- ταυτόχρονα βέβαια αυτό σημαίνει ότι αφού τρέχεις μέσα στη μέση του message loop πρέπει οτι είναι να κάνεις να το κάνεις γρήγορα. Με κάποιον άλλο timer δε θα ήταν έτσι τα πράγματα οπότε it needs to be said. Το Sleep και γενικοτερα τα blocked threads (WhaitSleepJoin) σπαταλουν resources. Οταν ενα thread γινεται blocked, το ThreadPool προσπαθει να διαθεσει αλλο ενα επιπλέον thread για να ικανοποιήσει την εφαρμογή ??? Αν ήταν άλλος θα έλεγα "καμία σχέση"... τι εννοείς; Το thread για το οποίο μιλούσαμε (Thread.Start) δεν έχει να κάνει με το managed thread pool (ThreadPool.QueueUserWorkItem). Ακόμα κι αν είχε, η δέσμευσή του για ένα τόσο μικρό χρονικό διάστημα και εφόσον δε γίνεται σε loop δεν θα προκαλέσει καμία αντίδραση. Και τέλος τι εννοείς "να ικανοποιήσει την εφαρμογή" αφού όπως φαίνεται η εφαρμογή έχει από μηδέν ως ελάχιστες απαιτήσεις από το thread pool? Κάθε thread θελει περιπου 1ΜΒ μνημη, οσο πιο πολλα threads τοσο περισσότερα context swiches. Μιλάμε για 1 μόνο thread οπότε το 1MB είναι άνευ σημασίας. Τα threads δεν προκαλούν αυτόματα context switches, ο scheduler τους δίνει χρόνο μόνο αν δεν είναι suspended (ή όπως το έλεγα παραπάνω αν είναι runnable). Όταν ένα thread κάνει sleep είναι suspended οπότε δεν προκαλεί κανένα context switch. Και τέλος το κόστος ενός context switch είναι πολύ σημαντικό αν γράφεις database server (γι' αυτό εκεί χρησιμοποιούν fibers/coroutines -- στην ουσία αναλαμβάνουν όλη την πολυπλοκότητα να κάνουν manually schedule το control flow ακριβώς γιατι το αυτόματο scheduling έρχεται με το κόστος του context switch) αλλά ανάξιο λόγου υπό ΚΣ. Αν εχεις μια εφαρμογη chat και δεν κανεις καλη χρηση των threads π.χ. διαθετεις 1 thread per user ο οποιος 99% του χρονου ειναι ανενεργος (δλδ το thread blocked) δεν μπορεις να εξυπηρετησεις πανω απο 20-25 ταυτοχρονα. Δεν το έχω δοκιμάσει αλλά χίλια τοις εκατό θα μπορέσεις να εξυπηρετήσεις πολλούς περισσότερους από 25 (δε βλέπω το λόγο να μη μπορεί το σύστημα να εξυπηρετήσει μερικές εκατοντάδες threads στο πολύ άνετο). Αυτή τη στιγμή το PC από το οποίο γράφω έχει ένα messenger process το οποίο τρέχει 63 threads (δεν έχω ιδέα τι κάνουν) ενώ δεν έχω καν το παράθυρό του ανοιχτό. Έχεις κάτι λάθος στην κοσμοθεωρία αλλά δεν είμαι σίγουρος τι ακριβώς.
παπι Δημοσ. 8 Απριλίου 2014 Δημοσ. 8 Απριλίου 2014 wpf ειναι? Τοτε δεν θες timer/thread θες animation
albNik Δημοσ. 9 Απριλίου 2014 Δημοσ. 9 Απριλίου 2014 Οντως τα blocked threads εξαιρουνται απο το context switch, και το 20-25 ηταν μικρο νουμερο, απλα θεωρω οτι ειναι σπαταλη να χρησιμοποιεις περισσοτερα threads (η για περισσοτερο χρονο) απ οτι χρειαζεσαι. Εχω επηρεαστει αρκετα απο το βιβλιο CLR via C# του Ritcher When a thread pool thread blocks, the thread pool creates additional threads, and the time and memory resources required to create, destroy, and schedule threads is very expensive. When many developers see that they have threads in their program that are not doing anything useful, they tend to create more threads in hopes that the new threads will do something useful. The key to building scalable and responsive applications is to not block the threads you have, so that they can be used and reused to execute other tasks
defacer Δημοσ. 9 Απριλίου 2014 Δημοσ. 9 Απριλίου 2014 Οντως τα blocked threads εξαιρουνται απο το context switch, και το 20-25 ηταν μικρο νουμερο, απλα θεωρω οτι ειναι σπαταλη να χρησιμοποιεις περισσοτερα threads (η για περισσοτερο χρονο) απ οτι χρειαζεσαι. Είναι, σ' αυτό συμφωνούμε, απλά εντάξει για το συγκεκριμένο παράδειγμα δε χάλασε κι ο κόσμος. Έκανα ένα τεστάκι στο linqpad πάντως και όντως φαίνεται πως μόλις μπλοκάρει ένα thread από το thread pool "αντικαθίσταται" άμεσα. void Main() { for (var i = 0; i < 10; ++i) { ThreadPool.QueueUserWorkItem(o => SleepingWorker()); Thread.Sleep(1000); } } // Define other methods and classes here delegate void Getter(out int workers, out int completion); void SleepingWorker() { Console.WriteLine("Thread {0} enter:", Thread.CurrentThread.ManagedThreadId); PrintAllCounts(); Thread.Sleep(9500); Console.WriteLine("Thread {0} exit:", Thread.CurrentThread.ManagedThreadId); PrintAllCounts(); } void PrintAllCounts() { PrintThreadCount("min", ThreadPool.GetMinThreads); PrintThreadCount("max", ThreadPool.GetMaxThreads); PrintThreadCount("available", ThreadPool.GetAvailableThreads); } void PrintThreadCount(string type, Getter getter) { int workers, completion; getter(out workers, out completion); Console.WriteLine("{0} threads: {1} workers, {2} completion", type, workers, completion); } Thread 12 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1022 workers, 1000 completion Thread 16 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1020 workers, 1000 completion Thread 13 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1019 workers, 1000 completion Thread 18 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1019 workers, 1000 completion Thread 19 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1018 workers, 1000 completion Thread 20 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1017 workers, 1000 completion Thread 21 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1016 workers, 1000 completion Thread 4 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1015 workers, 1000 completion Thread 22 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1014 workers, 1000 completion Thread 23 enter: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1013 workers, 1000 completion Thread 12 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1013 workers, 1000 completion Thread 16 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1014 workers, 1000 completion Thread 13 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1015 workers, 1000 completion Thread 18 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1016 workers, 1000 completion Thread 19 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1017 workers, 1000 completion Thread 20 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1018 workers, 1000 completion Thread 21 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1019 workers, 1000 completion Thread 4 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1020 workers, 1000 completion Thread 22 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1021 workers, 1000 completion Thread 23 exit: min threads: 4 workers, 4 completion max threads: 1023 workers, 1000 completion available threads: 1022 workers, 1000 completion When many developers see that they have threads in their program that are not doing anything useful, they tend to create more threads in hopes that the new threads will do something useful. WTF? (έχω και γω τον Richter αλλά δεν το θυμάμαι αυτό) "create more threads in hopes they will do something useful"? Εδώ σε στέλνει να σκεφτείς τι μπορεί να είδε και πόσο μπορεί να έφριξε γιατί φανερά "δε βγάζει νόημα"... The key to building scalable and responsive applications is to not block the threads you have, so that they can be used and reused to execute other tasks Εδώ τώρα μιλάει για την περίπτωση που έχεις έναν όγκο parallel εργασίας, οπότε σαφώς για να μεγιστοποιήσεις το throughput όσο λιγότερο κάνεις block τόσο καλύτερα (βλ. fibers/coroutines στον database server). Απλά στο συγκεκριμένο παράδειγμα έχουμε τη λίγο καγκούρικη εκδοχή του thread (χρήση σαν εκφυλισμένο timer) οπότε δε μας ενδιαφέρει τίποτα απ' όλα αυτά, δεν κάνουν και apply εξάλλου.
Portmaster Δημοσ. 9 Απριλίου 2014 Δημοσ. 9 Απριλίου 2014 Το παρακατω θα εμφανισει τη λεξη "whatever" για 3 δευτερολεπτα (το UI θα ειναι responsive). void ShowDelay(int ms) { label1.Text = "Whatewer"; var timer = new Timer() { Interval = ms }; timer.Start(); timer.Tick += (o, e) => { label1.Text = ""; timer.Dispose(); }; } private void button1_Click(object sender, EventArgs e) { ShowDelay(3000); } Το Sleep και γενικοτερα τα blocked threads (WhaitSleepJoin) σπαταλουν resources. Οταν ενα thread γινεται blocked, το ThreadPool προσπαθει να διαθεσει αλλο ενα επιπλέον thread για να ικανοποιήσει την εφαρμογή Κάθε thread θελει περιπου 1ΜΒ μνημη, οσο πιο πολλα threads τοσο περισσότερα context swiches. Αν εχεις μια εφαρμογη chat και δεν κανεις καλη χρηση των threads π.χ. διαθετεις 1 thread per user ο οποιος 99% του χρονου ειναι ανενεργος (δλδ το thread blocked) δεν μπορεις να εξυπηρετησεις πανω απο 20-25 ταυτοχρονα. Ενας γενικός κάνονας ειναι ότι αν κάτι θα αργήσει πανω απο 50ms δεν πρέπει να μπλοκάρεις (WhaitSleepJoin) περιμενοντας το, αστο να επιστρεψει (με callback) οταν τελειώσει. 00s called, they want their C# back... private async void Button_Click(object sender, EventArgs e) { label1.Text = "Whatewer"; await Task.Delay(1000); label1.Text = string.Empty; } Look ma no threads! 1
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα