Unbehandelte Ausnahme bei 0x00000000 - Access violation. beim multithreaden



  • Hi Leute 🙂
    Mein Programm war ursprünglich nicht fürs Multithreaden geplant, da ich aber nur einen Kern viel zu lang auslaste will ich jetzt umsteigen.
    ich probiere zum ersten Mal Multithreading zu benutzen aber blicke auch bis jetzt recht gut durch den Bspcode durch den ich gefunden habe.

    Allerdings kriege ich eine Fehlermeldung wenn ich das Programm ausführe:

    Unbehandelte Ausnahme bei 0x00000000 in OneElevenWithArrays.exe: 0xC0000005: Access violation.
    

    Könnt ihr mir vielleicht helfen?

    Hier ist der Codeabschnitt in dem der Fehler (beim 3. Durchlaufen der Schleife) auftritt:

    // ....
    // Wahrscheinlichkeit Gegner
    	for (int i = 0; i < 11; i++)
    	{
    		if(p2[i] > 0) // Hier der Fehler
    		{
    			hThread[i] = CreateThread( NULL, 
                                      0, 
                                      (LPTHREAD_START_ROUTINE)getWahrscheinlichkeit(p2, p1, PointsP2, PointsP1, i, false), 
                                      (LPVOID)i, 
                                      0, 
                                      &dwThreadID[i]
                                     ); 
    		}
    		else
    		{
    			if (!nullumgebung) std::cout << "=";
    			WahrscheinlichkeitGegner[i] = 0;
    		}
    	}
    // ....
    

    Hier die aufgerufene Methode getWahrscheinlichkeit:

    long WINAPI KI::getWahrscheinlichkeit(int* p1, int* p2, int PointsP1, int PointsP2, int i, bool ich)
    {
    	int NrofActiveCardsP1 = 0;
    	for (int j = 0; j < 11; j++)
    		if (p1[j] > 0)
    			NrofActiveCardsP1++;
    
    	if (NrofActiveCardsP1 > 10 || rdmKI)
    		return 0;
    	else
    	{
    		// egal er stirbt oben schon weil NrofActiveCards beim 1. Durchlauf immer 11 ist
    	}
    }
    

    Und hier die Headerdatei:

    #ifndef _KI_H
    #define _KI_H
    #include <string>
    #include <windows.h>
    #include "Globales.h"
    
    class KI
    {
    public:
    	KI(bool rdm, int nSchwierigkeit);
    	~KI(void);
    	void playCard(int* Wert, int* p1, int* p2, int PointsP1, int PointsP2);
    
    private:
    	long WINAPI getWahrscheinlichkeit(int* p1, int* p2, int PointsP1, int PointsP2, int i, bool ich);
    
    	int WahrscheinlichkeitGegner[11];
    
    	HANDLE hThread[200]; 
    	DWORD dwThreadID[200]; 
    
    };
    
    #endif
    


  • Benutz den Debugger.

    Du greifst auf einen Nullpointer zu und dereferenzierst ihn.



  • Hab ich schon
    Er springt bei diesem durch kommentare makierten Punkt raus aber ich versteh nicht warum 😕

    Was heißt ich greife auf einen Nullpointer zu und differenziere ihn? xD



  • Falsche Forum - WinAPI wäre besser.

    Der Cast nach LPTHREAD_START_ROUTINE verät, dass der Compiler nicht zufrieden war. Der Cast bringt zwar den Compiler zu schweigen - löst jedoch das Problem nicht. CreateThread(..) erwartet als drittes Argument ein Funktionspointer - wenn die Signatur korrekt ist brauchst Du auch den Cast nicht.

    Die Funktion muss so, und zwar genau so aussehen:

    DWORD WINAPI myThreadFunc(LPVOID param);
    

    (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686736.aspx)

    Ausserdem musst Du als drittes Argument eben einen Funktionspointer übergeben, das geht so (beachte dass es KEINE Klammern bei myThreadFunc braucht!):

    DWORD WINAPI myThreadFunc(LPVOID param)
    {
       return 0;
    }
    
    int main()
    {
       HANDLE hThread = CreateThread(NULL, 0, myThreadFunc, NULL, 0, NULL);
       WaitForSingleObject(hThread, INFINITE); // warten bis die myThreadFunc(..) ausgeführt wurde
       CloseHandle(hThread);
    }
    

    So, nun kommt wohl die logisch folgende Frage: Wie kann ich die Parameter übergeben, meine Berechnung druchführen und danach das Ergebnis wieder zurückgeben?

    Edit 1
    Als Hinweis noch - du solltest (von mir aus später) _beginthreadex(..) verwenden, da es zu Resourcen Leaks kommen kann bei der Verwendung von CreateThread(..) im Zusammenhang mit der C-Runtime.
    http://msdn.microsoft.com/en-us/library/kdzttdcb.aspx

    Edit 2
    Habe gerade bemerkt, dass dein Problem ja beim Code

    if(p2[i] > 0) // Hier der Fehler
    

    liegt - debugge es, wie schon angemerkt wurde. Meine Hinweise bezüglich CreateThread(..) etc. behalten natürlich trotzdem Gültigkeit!

    Edit 3
    Als Debug-Tip: Kommentiere die Zeile mit dem CreateThread(..) Aufruf aus und finde raus was in p2 drin ist - ev. kannst Du das auch hier zeigen - im Moment wissen wir das nämlich nicht.



  • Was genau ist LPVOID param?
    Einfach nur ein feststehender Ausdruck?

    Und kann ich iwie Parameter ubergeben?
    Oder muss ich das mit Klassenvariablen lösen? 🙂

    Aja mit den Variablen muss eig alles stimmen weil bevor ich multithreading eingeführt habe alles perfekt war 😉
    aber kann schon sein dass durch das Multithreading sich da was verändert auch wenn P2 nicht verändert wird eig

    das mim debugen werd ich ausprobieren sobald ich wieder am pc bin.
    Danke für die Hilfe schonmal! 🙂



  • LPVOID param ist überhaupt kein Ausdruck, sondern eben ein Parameter vom Typ LPVOID (also ein Zeiger auf beliebigen Inhalt).

    Aja mit den Variablen muss eig alles stimmen weil bevor ich multithreading eingeführt habe alles perfekt war

    Es kompiliert und läuft fehlerfrei, das heißt aber nicht, dass es immer fehlerfrei funktioniert. Das kann nur sichergestellt werden, wenn Du dich eben ohne Casten an die Definition der Funktion hältst.

    Parameter an die Funktion kannst Du über den Parameter arglist übergeben (siehe Doku.).



  • vomipodauszufaulzumlogin schrieb:

    Was genau ist LPVOID param?
    Einfach nur ein feststehender Ausdruck?

    Bei solchen Fragen hilft meistens MSDN:
    http://msdn.microsoft.com/en-us/library/aa383751.aspx

    vomipodauszufaulzumlogin schrieb:

    Und kann ich iwie Parameter ubergeben?
    Oder muss ich das mit Klassenvariablen lösen? 🙂

    Falls Du mit Klassenvariablen statisch Member Variablen der Klasse meinst - das geht technisch, ist aber der falsche (=beschränkte) Ansatz. nicht statische Membervariablen sind eine Lösung.

    vomipodauszufaulzumlogin schrieb:

    Aja mit den Variablen muss eig alles stimmen weil bevor ich multithreading eingeführt habe alles perfekt war 😉

    Das zweifle ich an - zumal Du es, soweit ich das beurteilen kann, nicht mal mit dem Debugger durchgesteppt bist. Beim gezeigten Code wird getWahrscheinlichkeit(..) sogar aufgerufen, nur nicht auf dem neuen, sondern im aktuellen Thread. Danach wird der Rückgabewert von getWahrscheinlichkeit(..) in ein LPTHREAD_START_ROUTINE gecastet - das kann nicht gut gehen.

    vomipodauszufaulzumlogin schrieb:

    das mim debugen werd ich ausprobieren sobald ich wieder am pc bin.
    Danke für die Hilfe schonmal! 🙂

    Sehr gut, das bringt dich weiter.



  • Zum Verständnis bis jetzt:
    ok also ich hab jetzt verstanden wie ein Threadaufruf aussehen muss und was LPVOID ist.
    Ich hab auch die Funktion getWahrscheinlichkeit(...) geändert zu

    DWORD WINAPI getWahrscheinlichkeit(LPVOID param);
    

    Zum Debuggen:
    Also in p2[i] müsste eig der richtige Wert drinnen sein, nämlich die Werte 1 bis 11.
    Ich hab ja erst das ganze ohne Multithreading gelöst und da musste ich oft genug mit dem Debugger durch die Funktion durchsteppen und dort schon gemerkt dass die Werte richtig sind 😉
    Aber sobald ich das Projekt erstellen kann werd ich nochmal durchsteppen!

    Noch nicht gelöste Probleme:
    1. Ich kriege jetzt einen neuen Fehler den ich nicht verstehe 😕
    Die Headerdatei:

    //...
    DWORD WINAPI getWahrscheinlichkeit(LPVOID param);
    //...
    

    Die cpp Datei

    // Wahrscheinlichkeit Gegner
    	for (int i = 0; i < 11; i++)
    	{
    		if(p2[i] > 0)
    		{
    			hThread[i] = CreateThread( NULL, 0, getWahrscheinlichkeit, NULL, 0, NULL); 
    		}
    		else
    		{
    			if (!nullumgebung) std::cout << "=";
    			WahrscheinlichkeitGegner[i] = 0;
    		}
    	}
    

    Aber jetzt unterringelt er mir getWahrscheinlichkeit rot und wenn ich mit der Maus drüberfahre gibt er mir folgendes aus:

    DWord __stdcall KI::getWahrscheinlichkeit(LPVOID param)
    Error: Das Argument vom Typ ""DWORD (__stdcall KI::*)(LPVOID param)"" ist mit dem Parameter vom Typ ""LPTHREAD_START_ROUTINE"" inkompatibel.
    

    Was heißt dieser Error? 😕

    2. Thread erst initialisieren dann starten:
    Habe ich das richtig verstanden, dass es klüger ist erst die Threads zu initialisieren und danach zu starten?
    Wenn ja mit welchem Befehl geht das?

    3. Parameterübergabe
    Ich habe immer noch nicht verstanden wie es am klügsten wäre der Funktion die ehmaligen Parameter

    //getWahrscheinlichkeit(int* p1, int* p2, int PointsP1, int PointsP2, int i, bool ich);
    

    zu übergeben 😕
    Wie würdet ihr das lösen und warum? 🙂

    Tausend dank dass ihr euch so geduldig mit meinem problem befasst! 🙂



  • Zu deiner Annahme, dass wenn es Single Threaded funktioniert auch Multi Threaded funktioniert ist leider nicht korrekt. Multi Threading ist wirklich eine harte Nuss.

    Zu 1:
    Das Problem ist, dass nur freie Funktionen oder statische Memberfunktionen für die geforderte Signatur in Frage kommen. KI::getWahrscheinlichkeit(..) muss also statisch (oder eine freie Funktion) sein.

    Zu 2:
    Ich würde die Threads beim erzeugen immer gerade starten.

    Zu 3:
    Das kannst Du über eine Klasse für die Argumente realieren. Der Trick ist, dass das Container Objekt mit den Argumenten genug lange lebt.

    Bsp.

    struct ThreadArguments
    {
       ThreadArguments(int myInt_, int* myPointer_)
          : myInt(myInt_)
          , myPointer(myPointer_)
       {}
       int myInt;
       int* myPointer;
       // ...
    };
    
    DWORD WINAPI myThreadFunc(LPVOID param)
    {
       ThreadArguments* threadArguments = reinterpret_cast<ThreadArguments*>(param);
       // benutze threadArguments->myInt, threadArguments->myPointer etc.
       return 0;
    }
    
    int main()
    {
       int* myPointer = 0;
       ThreadArguments threadArguments(27, myPointer);
       HANDLE hThread = CreateThread(NULL, 0, myThreadFunc, &threadArguments, 0, NULL);
       WaitForSingleObject(hThread, INFINITE); // warten bis die myThreadFunc(..) ausgeführt wurde
       CloseHandle(hThread);
    }
    

    Voraussetzung ist, dass das Objekt threadArguments solange lebt, wie is in myThreadFunc(..) gebraucht wird - hier wird das sichergestellt, indem nachher mit WaitForSingleObject(..) auf das Ende der myThreadFunc(..) Funktion gewartet wird.

    Eine andere Möglichkeit wäre das ThreadArguments Objekt mit new zu erzeugen - dann lebt es solange bis delete für das Objekt aufgerufen wird. Das könnte in der Thread Funktion geschehen, sobald es nicht mehr gebraucht wird. Diese Möglichkeit ist ein wenig komplexer (z.B. wenn CreateThread(..) fehlschlägt muss das ThreadArguments Objekt dort gelöscht werden).

    Als nächstes Thema solltest Du Dir die Synchronisation verschiedener Threads ansehen - könnte sein, dass dein Problem mit p2[..] was damit zu tun hat. Synchronisation ist nötig sobald mehrere Threads auf eine gemeinsame Resource (z.B. ein Objekt) zugreifen (diese Regel kann noch verfeinert werden, spielt aber hier vorerst keine Rolle).



  • @theta: Du willst nen static_cast, da es sich um eine Umwandlung von void* zu ThreadArguments* handelt.


Log in to reply