Threads + vector/...



  • Zu erst mal das: Ja, ich weiß das Threads nicht im C/C++ Standard enthalten sind - denke aber, dass das trotzdem das richtige Forum ist...

    Hier einfach mal ein Stück Quellcode, was in nem Thread abläuft, wo ich denke, dass da der Fehler ist...
    Die Fehlermeldung:
    'vector iterator not dereferencable'
    Ich solle in der Visual C++ Dokumentation nachlesen, was der Fehler sein könnte - aber leider hab ich keine Ahnung, wo genau (also eigtl hab ich scho nachgeguckt, aber hab nix gefunden, aber vll hab ich auch den falschen Verdacht...)

    Fkt: clientclass::client * user, const string * const tosend

    read_lock (); //is nur nen WaitFor (<handle>, INFINITE) -> darf mehrfach parallel ablaufen
    const vector <clientandrights>::iterator ende (Clients.end ());
    //typedef pair <clientclass::client *, unsigned __int8> clientandrights;
    for (vector <clientandrights>::iterator it = Clients.begin (); it != ende; ++it)
    	{
    		if ((*it).first != user)
    			{
    				((*it).first)->Sending (*tosend);
    			}
    	}
    read_unlock (); //SetEvent bzw den Counter, wie viele gerad Lesen, (um 1) dekrementieren
    

    Danke und sry für die wenigen Informationen, aber ich kann auch noch mehr posten, wenn ihr wollt - is nur eben so viel, was es theoretisch sein KÖNNTE - aber ich hoffe, wie gesagt, dass es das ist ^^

    Tom

    edit: hatte standard wie immer falsch geschrieben ^^



  • Bitte die genaue Fehlermeldung und schreib dazu in welcher Zeile.
    Mit Threads hat das im Übrigen nix zu tun, also dein Compiler-Fehler.
    p.S.: oder ist das ein Laufzeitfehler (evtl. vom MSVC-STL Iterator Debugging?)?



  • Also es ist kein Compiler-Fehler, sonst könnte ich es ja genau sagen, wo der Fehler liegt -.-
    Es ist also ein Laufzeitfehler!
    Es kommt auch ab und zu ein anderer vector-Fehler...
    Aber den bekomm ich gerad nicht mehr hin -.-
    'vector iterator not dereferencable' das ist und bleibt die genaue Fehlermeldung... Die Zeile, die er da angibt ist die Zeile 99 (oder so in etwa) aus irgend ner vector-datei... Das is au nur ne Fkt, die den Fehler an sich abfängt und ausgibt...

    Gerad eben hatte ich allerdings nur noch Zugriffsfehler - muss erst ma gucken, ob ich vorhin noch was geändert hatte um was anderes zu probieren...
    Es scheint aber zu 99% was mit den iteratoren und vectoren zu tun zu haben...

    Also:
    Kann man in ein und dem selben Array mehrfach zur (selben) Zeit lesen? (mittels dieser oder ähnlicher for-schleifen) Das hab ich nirgendwo rausfinden können... -.-

    Danke schon mal / noch mal ^^



  • unskilled schrieb:

    ...

    //is nur nen WaitFor (<handle>, INFINITE) -> darf mehrfach parallel ablaufen
    

    ...

    Ich vermute ja mal, dass diese Grundannahme falsch ist. Mich überrascht schon, dass Du keinen const_iterator in der Schleife verwendest; kann ein Hinweis darauf sein, dass das Ziel eben doch verändert wird. Das kann in parallelen Threads auf jeden Fall zu Problemen kommen.
    Leider sind solche Threadingprobleme echt fies .... so dass ich nichteinmal sicher wäre, ob der Fehler wirklich in diesem Codesegment liegt.

    Gruß,

    Simon2.



  • Du synchronisierst wahrscheinlich nicht richtig in deinem Programm. Da können wir dir aber nicht helfen, das musst du einfach richten dass es passt. Also so dass z.B. immer nur ein Thread gleichzeitig auf bestimmte Daten zugreift.



  • "Ich vermute ja mal, dass diese Grundannahme falsch ist."
    Nein, das ist nen WaitForSingleObject (notwrite, INFINITE) (er kann also so lange lesen, bis irgendwo ein Thread mal was schreiben will)
    Und die Annahme ist doch richtig, dass ich unendlich oft die gleiche Variable/... auslesen kann, so lang ich nicht schreib?!

    "Mich überrascht schon, dass Du keinen const_iterator in der Schleife verwendest"
    Doch? 'ende' ist ein const iterator...

    "kann ein Hinweis darauf sein, dass das Ziel eben doch verändert wird. Das kann in parallelen Threads auf jeden Fall zu Problemen kommen."
    ähh?! ja - es kann zu problemen kommmen, aber sollte durch das read_lock () am anfang nicht passieren...

    "Leider sind solche Threadingprobleme echt fies .... so dass ich nichteinmal sicher wäre, ob der Fehler wirklich in diesem Codesegment liegt."
    Allerdings... Wie oben beschrieben, bin ich mir eben au ne zu 100% sicher ^^

    Da nützt einem der Debugger auf einmal eben nix mehr - das ist bissl schade : < ^^

    "Du synchronisierst wahrscheinlich nicht richtig in deinem Programm."
    ich denke, das tu ich doch ^^

    "immer nur ein Thread gleichzeitig auf bestimmte Daten zugreift."
    Aber ich dachte immer, dass man so oft man lustig ist drauf zugreifen kann, so lang man nichts schreibt...
    also bis jz ist es bei mir so, dass ich unendlich oft lesen kann, bis irgend ein thread ma irgendwas schreiben muss... dann wartet der so lang, bis keiner mehr liest, schreibt und gibt danach wieder das ganze zeugs frei ^^



  • unskilled schrieb:

    ..."Mich überrascht schon, dass Du keinen const_iterator in der Schleife verwendest"
    Doch? 'ende' ist ein const iterator...

    Einerseits meinte ich nicht "ende", sondern "it" und andererseits keinen "const iterator" (der besagt, dass man den Iterator nicht mehr verändern darf), sondern einen "const_iterator" (der besagt, dass man das Objekt, auf das der Iterator verweist, nicht verändern darf).
    Ändere it mal in einen const_iterator um; wenn das durchgeht, dann ist die Methode "Sending() const" ... was bedeutet, dass sie clientandrights::first (und damit clientandrights) nicht ändert.
    Wenn das nicht funktioniert, greifst Du wahrscheinlich eben nicht nur lesend auf die vector-Elemente zu, sondern eben (via Sending()) doch verändernd.

    DIES kann dann dazu führen, dass Deine Grundannahme "...darf mehrfach parallel ablaufen..." nicht stimmt. Darauf wollte ich hinaus - ist jetzt hoffenlich klarer geworden.

    Gruß,

    Simon2.



  • danke : >
    klingt logisch ^^
    war zu hoch für mich ohne die weitere beschreibung - werd ich dann gleich ma versuchen

    edit:
    ok, const_iterator war scho ma kein schlechter hinweis ^^ hab das jz so geändert - es geht auf jeden fall noch... also war der "normale" iterator also überflüssig...

    jz hab ich wenigstens richtige zugriffsverletzungen, die total unlogisch sind -.-

    Danke erst mal - muss jz erst mal weitersuchen - stinkt voll, scheiß Threads ^^



  • "Du synchronisierst wahrscheinlich nicht richtig in deinem Programm."
    ich denke, das tu ich doch ^^

    Denke was du willst, ändert aber nix an den Tatsachen.

    Wie sieht denn "read_lock()" aus? Und wie sieht die entsprechende Funktion beim Schreiben aus ("write_lock()" vermute ich mal)?



  • so hatte ich mir das mal gedacht und eigtl dacht ich au, dasses geht ^^

    struct threads
    	{
    		private:
    			const HANDLE notwrite, notread, notrcount;
    			signed __int32 rcount;
    		protected:
    			threads (const bool isfree) : notwrite (CreateEvent (NULL, false, isfree, NULL)), notread (CreateEvent (NULL, false, true, NULL)), notrcount (CreateEvent (NULL, false, true, NULL)), rcount (0)
    				{};
    			void destruct (bool havetolock)
    				{
    					if (havetolock)
    						this->write_lock ();
    					CloseHandle (notwrite);
    					CloseHandle (notread);
    					CloseHandle (notrcount);
    				};
    		public:
    			void read_lock (void)
    				{
    					WaitForSingleObject (notwrite, INFINITE);
    					WaitForSingleObject (notrcount, INFINITE);
    					SetEvent (notwrite);
    					++rcount;
    					SetEvent (notrcount);
    				};
    			void read_unlock (void)
    				{
    					WaitForSingleObject (notrcount, INFINITE);
    					--rcount;
    					SetEvent (notrcount);
    				};
    			void write_lock (void)
    				{
    					WaitForSingleObject (notwrite, INFINITE);
    					WaitForSingleObject (notread, INFINITE);
    				};
    			void write_unlock (void)
    				{
    					SetEvent (notwrite);
    					SetEvent (notread);
    				};
    	};
    


  • hui, wo hast du denn das her (*schauder*)?

    mein tip: verwende entweder eine ganz normale mutex (nix reader/writer), oder verwende eine fertige reader/writer mutex. im CVS/SVN von boost ist z.B. was im entstehen (die version von der 1.33 hat bugs, in der 1.34 ist keine reader/writer mutex dabei, da die 1.33er eben verbuggt war).

    die spielerei mit den events von dir ist mit an sicherheit grenzender wahrscheinlichkeit falsch (ich hab aber gerade keinen geist da szenarien in gedanken durchzuspielen bis ich eines finde wo er fehler passiert).



  • also mutex is das beste da? (keine semaphores?!)

    mutex ist aber in wikipedia oä stets so definiert, dass niemals ein zweiter thread eine fkt aufrufen kann, die schon von einem anderen aufgerufen wird (auch dann, wenn er nur lesen muss)

    Ansonsten danke - und wenn du ma Zeit hast, kannste mir ja ma nen szenario geben, wo es fehler geben könnte?!

    Danke schon mal : >



  • Ganz einfach. Initial sind alle Events "signaled".
    Dann ruft ein Thread "read_lock" auf.
    1. "notwrite" geht auf non-signaled
    2. "notrcount" geht auf non-signaled
    3. "notwrite" geht wieder auf signaled
    4. "notrcount" geht wieder auf signaled
    Hier sind also wieder alle 3 Events signaled.
    Jetzt kommt ein Thread daher und ruft "write_lock" auf.
    Da alle 3 Events signaled sind läuft der da einfach durch.

    Mit anderen Worten: wenn ein "writer" daherkommt werden zwar neue "reader" blockiert, aber die "reader" die schon einen "read lock" haben verhindern nicht dass der "writer" einen "write lock" bekommt. Was normalerweise nicht das ist was man sich wünscht.

    also mutex is das beste da?

    Eine einfache Mutex oder eine sog. "Reader Writer Mutex" ist genau das was du brauchst.
    Eine einfache Mutex blockiert auch zusätzliche Reader, eine "Reader Writer Mutex" tut das nicht.

    Eine "Reader Writer Mutex" ist genau das was du versucht hast zu implementieren, bloss eben korrekt implementiert.

    Im übrigen: versuch mal einfach eine CRITICAL_SECTION (die schnellste fertige Mutex unter Windows). Ein Lock + Unlock auf eine CRITICAL_SECTION sollte typischerweise in einem Bruchteil der Zeit fertig sein der allein für einen einzigen WaitForSingleObject oder SetEvent Aufruf draufgeht. Anders gesagt: es kann leicht sein dass trotz der Beschränkung auf einen einzigen Reader das Programm mit einer einfachen CRITICAL_SECTION um einiges schneller läuft als mit deiner (nicht funktionierenden) "Reader Writer Mutex" Marke Eigenbau.

    Ansonsten kannst du mit Boost.Thread auch ganz einfach einen eigene (sehr einfache) Reader Writer Mutex zusammenbasteln:

    class ReaderWriterMutex
    {
    public:
    	ReaderWriterMutex() :
    		m_writeLockAcquired(0),
    		m_writersWaiting(0),
    		m_readerCount(0)
    	{
    	}
    
    	void WriteLock()
    	{
    		boost::mutex::scoped_lock l(m_mutex);
    		m_writersWaiting++;
    		while (m_readerCount || m_writeLockAcquired)
    			m_can_write_lock.wait(l);
    		m_writeLockAcquired = true;
    	}
    
    	void WriteUnlock()
    	{
    		boost::mutex::scoped_lock l(m_mutex);
    		m_writersWaiting--;
    		m_writeLockAcquired = false;
    		if (m_writersWaiting)
    			m_can_write_lock.notify_all();
    		else
    			m_can_read_lock.notify_all();
    	}
    
    	void ReadLock()
    	{
    		boost::mutex::scoped_lock l(m_mutex);
    		while (m_writersWaiting || m_writeLockAcquired)
    			m_can_read_lock.wait(l);
    		m_readerCount++;
    	}
    
    	void ReadUnlock()
    	{
    		boost::mutex::scoped_lock l(m_mutex);
    		m_readerCount--;
    		if (m_readerCount == 0)
    			m_can_write_lock.notify_all();
    	}
    
    private:
    	boost::mutex m_mutex;
    	boost::condition m_can_read_lock;
    	boost::condition m_can_write_lock;
    
    	bool m_writeLockAcquired;
    	size_t m_writersWaiting;
    	size_t m_readerCount;
    };
    

    p.S.: der Code ist ungetestet 🙂



  • Ich dümmlicher Dussel ^^
    Keine Ahnung, warum ich da ne mehr dran gedacht hab -.-

    Ja - Critical_Section war ich iwie zu doof für ^^ und als ich iwann ma gefragt hatte meinten die meisten, dass events (meist) der 'beste' Weg seien - dann guck ich mir das noch mal an... Danke!

    Danke für die gute Erklärung und den Code : )

    Ciao Tom


Anmelden zum Antworten