defacer Δημοσ. 7 Νοεμβρίου 2017 Δημοσ. 7 Νοεμβρίου 2017 Αφού κάνει implement IDictionary<> φυσικά και έχει απλά είναι explicit interface implementations (πρέπει να κάνεις cast στο interface ή απλά να βάλεις κατάλληλο type στο field). Γενικά όμως εξακολουθεί να έχει το θέμα ότι δε φαίνεται να είναι γραμμένο με συνέπεια. Να σου πω τι καταλαβαίνω εγώ από τον κώδικα και τη συζήτηση με τις μαντικές μου ικανότητες Ήθελες να κάνεις τα πράγματα async για ευκολία κλπ. Αλλά async σημαίνει ότι το threading δεν είναι στον έλεγχό σου οπότε χρειάστηκες synchronization στο dict. Έβαλες ConcurrentDict γιατί αυτός ήταν ο ευκολότερος τρόπος να πάρεις thread safety χωρίς να κάνεις απολύτως τίποτα. Αλλά εν τέλει έγραψες τη λογική σα να είχες κανονικό Dictionary στα χέρια σου γιατί αυτό είχες εξαρχής στο μυαλό σου πριν πάρεις την παράκαμψη με async. Αλλά δεν ξέρω τι θα έπρεπε να κάνεις "ιδανικά" γιατί λείπει context.
albNik Δημοσ. 7 Νοεμβρίου 2017 Δημοσ. 7 Νοεμβρίου 2017 Επίσης είναι αδύνατο να υπάρξει thread-safe collection (και γενικά class). Δηλαδή το παρακάτω δεν είναι safe ακόμα και αν οι μέθοδοι Contains και Remove είναι safe. if(mySuperThreadSafeCollection.Contains(key)) mySuperThreadSafeCollection.Remove(key); Ο client που χρησημοποιεί το object πρέπει να γράψει thread-safe κώδικα.
παπι Δημοσ. 7 Νοεμβρίου 2017 Μέλος Δημοσ. 7 Νοεμβρίου 2017 1,2,3 σωστα 4 δεν το καταλαβα Το context δεν ειναι περιπλοκο, εχουμε ενα rpc στο οποιο δεν μας ενδιαφερει το ordering. Μπορεις να κανεις 10 request και να παρεις 5-15 response. Ως εδω ολα καλα, το προβλημα ειναι οτι θελω ενα μικρο μερος απο αυτα τα rpc να εχουν response. Αυτο που σκεφτικα ειναι το snip που εδωσα πριν. Δηλαδη μια send η οποια βαζει ενα μοναδικο id στο packet (Αυτο που στελνει), αυτη εφοσον στειλει το πακετο, μπαινει σε inf loop και κανει lookup ενα dict εαν εχει packet με το id. παραλληλα σε ενα αλλο inf loop, αυτο που κανει recv και dispatch, τσεκαρω ολα τα πακετα που ερχονται αν εχουν το ιδιο id με αυτο του dict, αν δεν εχουν, γινονται dispatch, αν εχουν τοτε αντι να γινουν dispatch τα βαζω στο dict. Φυσικα αυτο σκεφτικα, αν ξες κανα pattern για αυτη την δουλεια, εδω ειμαι . Επίσης είναι αδύνατο να υπάρξει thread-safe collection (και γενικά class). Δηλαδή το παρακάτω δεν είναι safe ακόμα και αν οι μέθοδοι Contains και Remove είναι safe. if(mySuperThreadSafeCollection.Contains(key)) mySuperThreadSafeCollection.Remove(key); Ο client που χρησημοποιεί το object πρέπει να γράψει thread-safe κώδικα. Να το κανω με lock?
albNik Δημοσ. 7 Νοεμβρίου 2017 Δημοσ. 7 Νοεμβρίου 2017 Να το κανω με lock? Ναι, αν υπάρχει ενδεχόμενο το dictionary να αλλάξει από άλλο thread για όσο εκτελεις ακόμα και μόνο ένα non-thread-safe operation πχ dict[a]=b; Η όταν εκτελείς παραπάνω πάνω από ένα (έστω και thread-safe) operations μαζί.
solarpower Δημοσ. 7 Νοεμβρίου 2017 Δημοσ. 7 Νοεμβρίου 2017 Εμμ.... δεν είμαι σίγουρος αν καταλαβαίνεις τι λες. Δηλαδή αν έχω ένα dictionary από οτιδήποτε σε int απαγορεύεται να βάλω κάπου την τιμή μηδέν σε value αλλιώς κάνω λάθος χρήση; Αν έχω dictionary από οτιδήποτε σε οποιοδήποτε reference type απαγορεύεται να βάλω null αλλιώς κάνω λάθος χρήση; Και αυτό το συμπέρασμα προκύπτει επειδή νομίζεις από τη δική σου ερμηνεία της TryRemove ότι αντί να ελέγξεις την out parameter που έβαλαν εκεί ακριβώς γι' αυτό το σκοπό είναι "πιο πρέπον" να κοιτάξεις το return value? Sorry, get your shit together. Αυτά είναι απλώς αστεία. Δεν ξέρω τι ζόρι τραβάς αλλά εγώ αναφέρομαι στην οut parameter και εσύ με εγκαλείς γιατι κοιτάω το return value που είναι boolean...?? Με το συμπάθειο πάντα!
παπι Δημοσ. 8 Νοεμβρίου 2017 Μέλος Δημοσ. 8 Νοεμβρίου 2017 Ναι, αν υπάρχει ενδεχόμενο το dictionary να αλλάξει από άλλο thread για όσο εκτελεις ακόμα και μόνο ένα non-thread-safe operation πχ dict[a]=b; Η όταν εκτελείς παραπάνω πάνω από ένα (έστω και thread-safe) operations μαζί. Κοίτα, στο ίδιο key value δεν υπάρχει περίπτωση να υπάρξει write/delete από δύο thread
defacer Δημοσ. 8 Νοεμβρίου 2017 Δημοσ. 8 Νοεμβρίου 2017 1,2,3 σωστα 4 δεν το καταλαβα Χμ πώς να το περιγράψω... είναι λίγο σαν τη γνωστή ιστορία με το inheritance. Κάποιος κάνει inherit για να κάνει code reuse, αλλά δε λαμβάνει "σοβαρά" υπόψη πως η κίνηση του να κάνεις inherit συνεπάγεται πράγματα που πρέπει να γίνονται ή να μη γίνονται, τα οποία δεν έχουν καμία σχέση με την ιδέα του code reuse από μόνα τους. Οπότε βλέπεις μετά αρχάριους να τα κάνουν πουτάνα επειδή ήθελαν code reuse και ψώνισαν inheritance χωρίς να έχουν σκεφτεί αν όντως θέλουν ή τι συνέπειες έχει το inheritance. Έτσι και δω μου φαίνεται ότι κατέληξες με το concurrent dict για ευκολία, αλλά δε συνέχισες προσαρμόζοντας τη λογική του κώδικα στο γεγονός ότι το dict αυτό είναι σχεδιασμένο για να δουλεύει με multithreaded λογική, εξού και ότι σου δίνει φόρα παρτίδα τα Try methods και τα "κλασικά" τα κρύβει σαν explicit interface implementation. Οπότε νομίζω πως το καλύτερο θα ήταν είτε να αλλάξεις σε κάποια άλλη λύση από concurrent dict, είτε να γράψεις τον κώδικα σα να υπήρχαν Ν threads που δουλεύουν το dict παράλληλα. Διαφορετικά ο,τι κι αν κάνεις θα φαίνεται περίεργη η φάση. Δε μιλάμε τώρα πολλή δουλειά, είναι λίγο δε θέλει κόπο θέλει τρόπο. Δεν ξέρω τι ζόρι τραβάς αλλά εγώ αναφέρομαι στην οut parameter και εσύ με εγκαλείς γιατι κοιτάω το return value που είναι boolean...?? Με το συμπάθειο πάντα! Τα μπέρδεψα λίγο με το ποιό είναι η επιστρεφόμενη τιμή και ποιό το out parameter καθώς δεν κοιτούσα method signature, αλλά αυτό δεν έχει πρακτικά σημασία. Ο λόγος που σε εγκάλεσα είναι το μαργαριτάρι που άφησες με τις default values στα dicts, το οποίο είναι παραπληροφόρηση και το διαβάζουν και αρχάριοι.
solarpower Δημοσ. 8 Νοεμβρίου 2017 Δημοσ. 8 Νοεμβρίου 2017 (επεξεργασμένο) Σίγουρα ο καθένας βάζει ότι θέλει σε ένα Dict. Αλλά όταν του λένε ότι το συγκεκριμένο φέρνει την default τιμή όταν δεν υπάρχει το κλειδί, τότε γιατί να χρησιμοποιήσεις default τιμή; Κάποιος που θα χρησιμοποιήσει το συγκεκριμένο Dict και θέλει να διαγράψει ένα κλειδί, επειδή το έχει ως flag, δηλαδή τον ενδιαφέρει η ύπαρξή του στο Dict και μόνο, θα κοιτάξει: Πρώτα αν γυρνάει true, που σημαίνει ότι έγινε ο έλεγχος (γιατί false σημαίνει ότι δεν μπορεί να πειράξει το Dict), και μετά θα κοιτάει αν η επιστρεφόμενη (out) τιμή είναι η default ή όχι. Αν είναι η default τότε σίγουρα δεν υπήρχε το flag, και δεν το έχει αφαιρέσει. Αν είναι κάτι άλλο, τότε σίγουρα υπάρχει το flag και το αφαίρεσε. Τώρα αν θες να κάνεις την εισαγωγή του flag με default τιμή, πώς θα ξέρεις αν υπήρχε ή όχι το flag, και ταυτόχρονα ότι έχει αφαιρεθεί; Θα πρέπει να το κάνεις σε δυο χρόνους, και αυτό σημαίνει να κλειδώσεις το dict, να κάνεις search, να πάρεις το οκ, και μετά να το διαγράψεις, και να το απελευθερώσεις. Να γιατί γράφω ότι είναι λάθος τρόπος αυτός. Όχι ότι δεν γίνεται, αλλά λάθος ως προς την λειτουργία που ήδη σου προσφέρει, και δεν πας σε lock.. Επεξ/σία 9 Νοεμβρίου 2017 από solarpower
defacer Δημοσ. 9 Νοεμβρίου 2017 Δημοσ. 9 Νοεμβρίου 2017 Σίγουρα ο καθένας βάζει ότι θέλει σε ένα Dict. Αλλά όταν του λένε ότι το συγκεκριμένο φέρνει την default τιμή όταν δεν υπάρχει το κλειδί, τότε γιατί να χρησιμοποιήσεις default τιμή; α) Γιατί η default τιμή είναι κι αυτή μια τιμή όπως όλες οι άλλες που δεν κατούρησε στο πηγάδι και μπορεί να τη χρειάζεσαι. β) Γιατί σου δίνουν άλλο μηχανισμό για να κάνεις τα πράγματα που σχετίζονται με "υπάρχει η τιμή ή όχι". Αν αυτό θέλεις να κάνεις, χρησιμοποιείς αυτό το μηχανισμό. Αν χρησιμοποιείς άλλο μηχανισμό για να κάνεις αυτό το πράγμα απλά μπερδεύεις τον αναγνώστη. POLA. Κάποιος που θα χρησιμοποιήσει το συγκεκριμένο Dict και θέλει να διαγράψει ένα κλειδί, επειδή το έχει ως flag, δηλαδή τον ενδιαφέρει η ύπαρξή του στο Dict και μόνο, θα κοιτάξει: Αυτός ο κάποιος χρησιμοποιεί το λάθος data structure για τη δουλειά που κάνει και θα έπρεπε να χρησιμοποιεί αντί για dict κάποιο set και τότε δε θα υπήρχε απολύτως κανένα θέμα να συζητήσουμε. Πρώτα αν γυρνάει true, που σημαίνει ότι έγινε ο έλεγχος (γιατί false σημαίνει ότι δεν μπορεί να πειράξει το Dict), και μετά θα κοιτάει αν η επιστρεφόμενη (out) τιμή είναι η default ή όχι. Αν είναι η default τότε σίγουρα δεν υπήρχε το flag, και δεν το έχει αφαιρέσει. Αν είναι κάτι άλλο, τότε σίγουρα υπάρχει το flag και το αφαίρεσε. Τώρα αν θες να κάνεις την εισαγωγή του flag με default τιμή, πώς θα ξέρεις αν υπήρχε ή όχι το flag, και ταυτόχρονα ότι έχει αφαιρεθεί; Θα πρέπει να το κάνεις σε δυο χρόνους, και αυτό σημαίνει να κλειδώσεις το dict, να κάνεις search, να πάρεις το οκ, και μετά να το διαγράψεις, και να το απελευθερώσεις. Να γιατί γράφω ότι είναι λάθος τρόπος αυτός. Όχι ότι δεν γίνεται, αλλά λάθος ως προς την λειτουργία που ήδη σου προσφέρει, και δεν πας σε lock.. Είναι λάθος τρόπος πολύ πριν μπούμε στη συζήτηση του τι μπορείς να κάνεις και πως επειδή λάθος data structure. Εδώ τώρα αν καταλαβαίνω τι λες, μιλάς για το σενάριο "θέλω να κάνω remove αλλά μόνο αν το value είναι κάτι συγκεκριμένο". Δεν υπάρχει έτοιμο collection που να το προσφέρει αυτό ως thread safe atomic operation, nothing wrong with using locks αν πραγματικά το χρειάζεται κανείς αυτό, αλλά ανάλογα με τη νοοτροπία του προγράμματος δε χρειάζεται κιόλας. Βασικά το πρόβλημα με αυτό το what if scenario είναι ότι ξεκινάς έχοντας μπει ήδη σε XY-problem-land: προσπαθείς να σκεφτείς πώς θα γίνει να δουλέψει μια συγκεκριμένη υποθετική λύση χωρίς πρώτα να έχουμε συμφωνήσει πως το τάδε πρόβλημα και θα έπρεπε να λυθεί με το συγκεκριμένο τρόπο (δηλαδή collection και μοντέλο χρήσης του). Για παράδειγμα, ένας τρόπος να πετύχεις το ίδιο αποτέλεσμα logically -- χωρίς να εξετάσω αν αυτό το αποτέλεσμα είναι κάτι που θα έπρεπε να επιδιώξουμε -- είναι να μην κάνεις ποτέ Remove (ή τέλος πάντων να γίνεται υπό άλλες συνθήκες) και να αφήσεις μέσα τα entries με default value το οποίο στο σενάριο που περιγράφεις είναι εξίσου αποτελεσματικό (το κάνεις treat σα να μην υπάρχει). Μπορεί να μην υπάρχει έτοιμο atomic conditional removal, αλλά υπάρχει έτοιμο atomic conditional update που υλοποιεί στην ουσία CAS πάνω στο συγκεκριμένο dictionary. TL;DR: ΧΥ problems are problematic.
solarpower Δημοσ. 10 Νοεμβρίου 2017 Δημοσ. 10 Νοεμβρίου 2017 1. To ISet<T> interface δεν είναι thread safe, Αυτά που είναι αναφέρονται εδώ: https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent?view=netframework-4.7.1 2. Αυτό που γράφεις εδώ "θέλω να κάνω remove αλλά μόνο αν το value είναι κάτι συγκεκριμένο" είναι λάθος διατυπωμένο, σε σχέση με αυτό που περιγράφω: Θέλω να κάνω remove, αν υπάρχει, ώστε άλλα thread που θα προσπαθήσουν, δεν θα το πάρουν και αντί αυτού θα πάρουν την default τιμή, και όχι αυτό που έβγαλα. Η default τιμή δεν είναι λάθος τιμή αλλά είναι μια τιμή που σίγουρα δεν είναι χρήσιμη, και κάνει το loop να συνεχίζει μέχρι να έρθει το σωστό ή κάποιο άλλο συμβάν να το τερματίσει. Η σωστή διατύπωση είναι: "θέλω να κάνω remove, αν το βρω, αλλά αν το value είναι default σημαίνει ότι θα συνεχίσω να το ψάχνω, μέχρι κάποιο άλλο thread να το γυρίσει πίσω, και ενδεχομένως να του έχει αλλάξει τιμή, αλλά όχι την defaut τιμή γιατί δεν θα έχει νόημα να ψάχνω..."
defacer Δημοσ. 10 Νοεμβρίου 2017 Δημοσ. 10 Νοεμβρίου 2017 Το interface από μόνο του δε μπορεί να είναι thread safe. Κάποιο implementation μπορεί να είναι ή όχι. Όπως και να χει, πρώτα βρίσκεις το κατάλληλο data structure και μετά κοιτάς το thread safety. Αν το πας ανάποδα όπως το παπί εδώ αρχίζουν τα μπερδέματα, γιατί η επιλογή του data structure (και άρα του interface του) καθορίζει το στυλ στο οποίο θα πρέπει να γραφτεί ο κώδικας. Anyway, το είπα τόσες πολλές φορές που δεν έχει νόημα να ξαναλέω τα ίδια. Στην προκειμένη περίπτωση θα μπορούσαν να γίνουν χίλια δύο διαφορετικά πράγματα, π.χ. τι νόημα έχει το dictionary αφού στην ουσία έχεις ένα producer/consumer queue. Ένα απλό ConcurrentBag (ή για φανταιζί BlockingCollection) θέλει μόνο, πετάς μέσα ένα Tuple<int, Packet>, route τα packets με όποια σειρά τυχαίνει να βγουν και τέλος.
albNik Δημοσ. 10 Νοεμβρίου 2017 Δημοσ. 10 Νοεμβρίου 2017 Πιστευω ότι χρησιμοποιεί (concurrent) dictionary επειδή θέλει να αφαιρεί τα πακέτα κατά Id και όχι με τη σειρά που έρχονται. Η TryRemove δεν χρειάζεται έλεγχο αν υπάρχει το στοιχείο άρα ούτε και lock. To lock υπάρχει μέσα στην TryRemove. Η return value θα σου πει αν πράγματι υπήρξε το object που πήρες με out. 1
defacer Δημοσ. 10 Νοεμβρίου 2017 Δημοσ. 10 Νοεμβρίου 2017 Concurrent dict είπαμε χρησιμοποιεί επειδή βόλευε να μην είναι thread τελείως unsafe, δες παραπάνω post μου που επιβεβαίωσε το παπί με "1,2,3 σωστα".
παπι Δημοσ. 11 Νοεμβρίου 2017 Μέλος Δημοσ. 11 Νοεμβρίου 2017 Χμ πώς να το περιγράψω... είναι λίγο σαν τη γνωστή ιστορία με το inheritance. Κάποιος κάνει inherit για να κάνει code reuse, αλλά δε λαμβάνει "σοβαρά" υπόψη πως η κίνηση του να κάνεις inherit συνεπάγεται πράγματα που πρέπει να γίνονται ή να μη γίνονται, τα οποία δεν έχουν καμία σχέση με την ιδέα του code reuse από μόνα τους. Οπότε βλέπεις μετά αρχάριους να τα κάνουν πουτάνα επειδή ήθελαν code reuse και ψώνισαν inheritance χωρίς να έχουν σκεφτεί αν όντως θέλουν ή τι συνέπειες έχει το inheritance.Έτσι και δω μου φαίνεται ότι κατέληξες με το concurrent dict για ευκολία, αλλά δε συνέχισες προσαρμόζοντας τη λογική του κώδικα στο γεγονός ότι το dict αυτό είναι σχεδιασμένο για να δουλεύει με multithreaded λογική, εξού και ότι σου δίνει φόρα παρτίδα τα Try methods και τα "κλασικά" τα κρύβει σαν explicit interface implementation.Οπότε νομίζω πως το καλύτερο θα ήταν είτε να αλλάξεις σε κάποια άλλη λύση από concurrent dict, είτε να γράψεις τον κώδικα σα να υπήρχαν Ν threads που δουλεύουν το dict παράλληλα. Διαφορετικά ο,τι κι αν κάνεις θα φαίνεται περίεργη η φάση. Δε μιλάμε τώρα πολλή δουλειά, είναι λίγο δε θέλει κόπο θέλει τρόπο.Τα μπέρδεψα λίγο με το ποιό είναι η επιστρεφόμενη τιμή και ποιό το out parameter καθώς δεν κοιτούσα method signature, αλλά αυτό δεν έχει πρακτικά σημασία.Ο λόγος που σε εγκάλεσα είναι το μαργαριτάρι που άφησες με τις default values στα dicts, το οποίο είναι παραπληροφόρηση και το διαβάζουν και αρχάριοι. Για inh που λες, παίζει ναναι η πρώτη φορά που μου βγαίνει. Το αστείο, το εφάρμοσα για encapsulation και όχι για reuse. Όσο για το αρχικό θέμα, το αφήνω ως έχει και βλέπουμε.
solarpower Δημοσ. 11 Νοεμβρίου 2017 Δημοσ. 11 Νοεμβρίου 2017 Όπως το σκέφτομαι το θέμα, το dict αυτό είναι πιο γενικό, από το να βάζει κανείς πράγμα που κανονικά είναι για ένα thread. (και νομίζω αυτό λέει και ο Defacer). Θα έβαζα δηλαδή ένα κλειδί για κάθε thread στο dict, και σαν τιμή ένα SortedDictionary για τα πακέτα. Αυτό θα είχε περισσότερο νόημα. Όσο για το μηχανισμό του tryremove, είναι χρήσιμο εδώ γιατί πριν βάλει κάτι κανείς στο SortedDictionary, το βγάζει από το Dict, το αλλάζει και το ρίχνει πίσω ;Όταν ένα queue ολοκληρωθεί, τότε το βάζουμε σε ένα άλλο dict για κατανάλωση από άλλο thread, και εντέλει τα πρώτα τα θέλουμε για να έχουν μια ανάδραση του "ποσοστού" αν γνωρίζουμε το μέγεθος, ή ενός μηνύματος "κατεβάζω".
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα