Globales Objekt in mehreren Threads verwenden?

Pain-maker

Mitglied

Hi @ all!

Zu Beginn vielleicht folgender (vereinfachter) Codeauszug:
Code:
#include <ScriptEngine>

static ScriptEngine *vbSc = NULL;

void initEngine()
{
	vbSc = new ScriptEngine();
}

int loadScript(const std::string filePath)
{
	return vbSc->loadScript(filePath);
}

void exitEngine()
{
	delete vbSc;
}

Nun zu meinem Problem:
Ich versuche ein Objekt (ScriptEngine) über initEngine() aus Thread A zu initialiseren. In dem gleichen Thread wird dieses dann auch später wieder über exitEngine() zerstört.
Dann habe ich einen Thread B, der auf das Objekt über loadScript() zugreift.
So lange ich loadScript() in Thread A ausführe, läuft alles Problemlos. Erst sobald ich versuche aus Thread B darauf zuzugreifen, hagelt's Exceptions.

Warum das so ist ist mir klar, da ich einen unerlaubten Threadübergreifenden Zugriff auf das Objekt mache.

Nun wäre aber meine Frage, wie ich das denn am besten lösen könnte?
Oder wie wäre es denn am "sichersten"?

Vielen Dank :)
Mfg Pain-maker
 
Zuletzt bearbeitet:
Hi.

Meinst du das du gleichzeitig von verschiedenen Threads auf vbSc zugreifen willst?

Dann mußt du mit einem Mutex gegenseitigen Ausschluß realisieren wenn die Methoden der Scriptengine selbst nicht threadsicher sind.

Siehe CreateMutex und WaitForSingleObject. Boost.Thread würde die ganze Sache noch vereinfachen.

Gruß
 
Nun die Aufrufe erfolgen nacheinander.
Das einzige Problem dabei ist, das das Objekt vbSc aus einem anderen Thread über loadScript() benutzt wird, in dem es nicht erzeugt wurde.
(Ich bekomme dann natürlich eine AccessViolationReadingAdress-Exception)

Ich hab schon nach eine Art "shared object" gesucht, aber nichts gefunden...
 
Zuletzt bearbeitet:
Nun die Aufrufe erfolgen nacheinander.
Das einzige Problem dabei ist, das das Objekt vbSc aus einem anderen Thread über loadScript() benutzt wird, in dem es nicht erzeugt wurde.
(Ich bekomme dann natürlich eine AccessViolationReadingAdress-Exception)
Das würde bedeuten, das der Zeiger nicht initialisiert wurde und hat nichts mit dem Threads an sich zu tun.

Wie startest du die Threads? Zeig deinen Code. Vermutlich erfolgen die Aufrufe in den Threads doch nicht nacheinander?!

Gruß
 
Hmm... da hängt noch zu viel anderes drin um den ganzen Code zu posten.
Aber vielleicht erklärts das besser:

Code:
static ScriptEngine *vbSc = NULL;

void initEngine()
{
	vbSc = new ScriptEngine();
}

int loadScript(const std::string filePath)
{
	return vbSc->loadScript(filePath);
}

void exitEngine()
{
	delete vbSc;
}

// GUI (bzw. Hauptthread)
void GUIThread()
{
	// Beim Programmstart initialiseren
	initEngine();
    
	// Programm läuft...
	while(GUIThreadStateRunning)
	{
	    (...)
	}
    
	// Beim Beenden zerstören
	exitEngine();
}

// Callback-Funktion eines Buttons (ANDERER Thread)
void cb_ButtonClicked()
{
	// Script ausführen
	loadScript(targetFile);
}

Ich habe einen Hauptthread in dem auch die GUI läuft. Dort will ich die Engine global initialisieren und beenden, so dass das nicht bei jedem Skriptaufruf passiert.
Allerdings erfolgen die eigentlichen Scriptaufrufe in einer Callback-Funktion eines Buttons, die in einem eigenen Thread abläuft.

Beschreibt's das besser?
 
Hallo,

für mich liest sich das jetzt so, dass die Klasse ScriptEngine Aufrufe aus einem anderen Thread außer dem, der die jeweilige Instanz erzeugt hat, nicht erlaubt. Soll heißen, die Klasse stammt aus einer externen Bibliothek, welche Pain-maker verwendet. Ohne weitere Informationen (woher kommt die Klasse?) ist das aber auch nur eine Vermutung.

Grüße, Matthias
 
Also ich hab jetzt eine Lösung gefunden. Die stellt mich zwar nicht hundertprozentig zufrieden, aber es funktioniert klasse :)
Der Ansatz mit der Singleton-Klasse war gut!

Ich benutze jetzt eine Wrapper-Klasse, die vbSc hält und der ich einfach ein neues Script in die Warteschlange zur Ausführung gebe.

Code:
/**
 * ScriptLoader
 */
class ScriptLoader
{
private:
	voltaile int threadState = THREADSTATUS_RUNNING;
	voltaile int execStatus  = 0;
	std::string  targetFile  = "";
	std::string  output      = "";
	ScriptEngine *vbSc       = NULL;

public:
	ScriptLoader()
	{
		this->execStatus = 0;
		this->targetFile = "";
		this->output     = "";
		// Thread starten
		os_StartThread(&ScriptLoaderLoop, this);
	}
	
	~ScriptLoader()
	{
		// Thread stoppen
		this->threadState = THREADSTATUS_PLEASE_STOP;
	}
	
	// Thread-Loop
	static int ScriptLoaderLoop()
	{
		// Engine starten
		this->vbSc = new ScriptEngine();
		
		while(this->threadState == THREADSTATUS_RUNNING)
		{
			// Script ausführen, wenn ein neues ansteht
			if(this->execStatus == 1)
			{
				this->output = this->vbSc->loadScript(this->targetFile);
				// Status zurücksetzen
				this->execStatus = 0;
				this->targetFile = "";
			}
			// Sleep gegen CPU-Überlastung
			Sleep(10);
		}
		
		// Engine stoppen
		delete this->vbSc;
		
		return 0;
	}
	
	// Neues Script "anmelden"
	void execScript(const std::string filePath)
	{
		this->targetFile = filePath;
		this->execStatus = 1;
	}
	
	// Script geparsed?
	bool isBusy()
	{
		return (this->execStatus == 1);
	}

	// Ergebnis holen
	std::string getOutput()
	{
		return this->output;
	}
};

/**
 * Hauptprogramm
 */
 // Globales Handel
ScriptLoader scl;

// GUI (bzw. Hauptthread)
void GUIThread()
{
	// Programm läuft...
	while(GUIThreadStateRunning)
	{
	    (...)
	}
}

// Callback-Funktion eines Buttons (ANDERER Thread)
void cb_ButtonClicked()
{
	// Ausführen
	scl.execScript(targetFile);
	// Auf Ergebnis warten
	while(scl.isBusy())
	{
		Sleep(10);
	}
	// Output behandeln
	std::string output = scl.getOutput();
}

Vielen Dank!
 
Zuletzt bearbeitet:

Neue Beiträge

Zurück