CStdioFile - Zugriff von mehreren Threads
-
Hi,
ich habe ein .txt file, das während der ganzen Laufzeit geöffnet ist.
ich öffne es in main. dann starte ich noch weitere threads die auch darauf zugreifen. Jeder soll eine Zeile einlesen und weiter verarbeiten.
wenn ich 20 threads benutze, funktioniert es, wenn ich es mit ~100 versuche, bekomme ich einen error: "Die Anweisung in 0x7c... verweist auf den Speicher 0x0.... Der Vorgang "read" konnte nicht auf dem Speicher durchgeführt werden."
Wie kann man das lösen?
Danke schonmal
Popogas
-
Hi,
das liegt nicht daran, das du 20, 100 oder 2 Threads hast. Wenn mehrere Threads
auf ein Objekt zugreifen und du den Zugriff nicht syncronisierst, spielst
du russisches Roulette!Du must den Zugriff auf das CStdioFile-Objekt Syncronisieren. Schau dir
dazu die Klasse CCriticalSection in der MSDN an.Gruss
EB
-
Ok danke.
Hab mir das alles mal in der msdn angeschaut und durchgelesen.
ich hab es theoretisch verstanden.
Es gibt nurnoch ein problem, ich weiß nicht wie ich es initialisiere.Im folgenden Codebeispiel wird diese Methode anhand des m_CritSection-Datenmembers (vom Typ CCriticalSection) demonstriert, das in der gemeinsam genutzten Ressourcenklasse deklariert wurde;
(http://msdn.microsoft.com/library/deu/default.asp?url=/library/DEU/vccore/html/_core_multithreading.3a_.how_to_use_the_synchronization_classes.asp)
das steht in der msdn.Welches ist die Ressourcenklasse?
bzw kann vllt jemand n ganz kurzes codebeispiel geben, das wär super
sry für diese noobigen fragen, aber ich sitz da schon länger dran, und habs noch nich geschafft.
-
Hi,
du kannst eine Klasse von CStdioFile ableiten z.B CSharedFile.
Darin machst du eine protected Member Varialble vom Typ CCriticalSection.
Die wird im Konstruktor initialiert und im Destruktor zerstört.
In den Konstruktor und den Destruktor natürlich die Basis-Konstruktoren bzw.
Destruktoren aufrufen.
Dann implementierst du alle Methoden von StdioFile, welche du benötigst.
Dann "lockst" du das CriticalSection objekt, rufst die entsprechende Funktion
in der Basisklasse auf, "delockst" das Objekt und gibst die Rückgabe der Funktion zurück.
Du kapselst also jede public Funktion die du benötigst. Du solltest daher
deine Klasse protected von CStdioFile ableiten statt public.
Damit ist dann die Klasse Threadsicher.Ich hoffe das hilft weiter. Sonst Poste deinen Code wenn du nicht weiter kommst.
Gruss
EB
-
Danke schonmal.
ich denk das schaffe ich so, ist ja ne relativ genaue beschreibung.
ich melde mich nochmal wenn ich noch n problem hab, oder poste hier mal mein code.
man findet net so viele gute beispiele dazu
-
ich habs mit hilfe von dem msdn beispiel versucht.
scheint aber noch nicht ganz zu stimmen, ich bekomme immer "programm hat ein fehler festgestellt, muss beendet werden" beim starten.
class CSharedFile { static CCriticalSection m_cs; static CStdioFile inputfile; public: CSharedFile(void); static CString leseString(); ~CSharedFile(void); }; CStdioFile CSharedFile::inputfile; CCriticalSection CSharedFile::m_cs; CString CSharedFile::leseString() { m_cs.Lock(); CString str; inputfile.ReadString(str); m_cs.Unlock(); return str; }
aus einer anderen funktion dann ->
while(sIP = CSharedFile::leseString() ) { // do sth }
hab dabei bissel hier geschaut: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_ccriticalsection.3a3a.lock.asp
-
Hi,
hier mal ein Ansatz:
Header:
#pragma once #include <afxmt.h> // mfc sync object class CAutoLock // Hilfsklasse für CriticalSection { public: CAutoLock(CCriticalSection& cs); // Lock Objekt ~CAutoLock(); // Unlock Objekt private: CCriticalSection& m_cs; // reference auf Lock-Objekt }; // CSafeStdioFile command target class CSafeStdioFile : protected CStdioFile // protected ableiten, damit Client nur unsere Threadsicheren Methoden aufrufen kann! { public: CSafeStdioFile(FILE* pOpenStrem); CSafeStdioFile(LPCTSTR lpszFileName, UINT nOpenFlags); virtual ~CSafeStdioFile(); virtual LPTSTR ReadString(LPTSTR lpsz, UINT nMax); virtual BOOL ReadString(CString& rString); protected: CCriticalSection m_cs; // Lock Objekt };
Implementierung:
// SafeStdioFile.cpp : implementation file // #include "stdafx.h" #include "SafeStdioFile.h" // CAutoLock CAutoLock::CAutoLock(CCriticalSection& cs) : m_cs(cs) { m_cs.Lock(); } CAutoLock::~CAutoLock() { m_cs.Unlock(); } // CSafeStdioFile CSafeStdioFile::CSafeStdioFile(FILE* pOpenStream) : CStdioFile(pOpenStream) { } CSafeStdioFile::CSafeStdioFile(LPCTSTR lpszFileName, UINT nOpenFlags) : CStdioFile(lpszFileName, nOpenFlags) { } CSafeStdioFile::~CSafeStdioFile() { } // CSafeStdioFile member functions LPTSTR CSafeStdioFile::ReadString(LPTSTR lpsz, UINT nMax) { CAutoLock lock(m_cs); // lock die Section return CStdioFile::ReadString(lpsz, nMax); // Rufe Basisklasse auf // CAutoLock wird zerstört und gibt die Section wieder frei! } BOOL CSafeStdioFile::ReadString(CString& rString) { CAutoLock lock(m_cs); return CStdioFile::ReadString(rString); }
Gruss
EB
-
Ok danke hab es in mein prog eingebaut.
stimmt es wenn ich es dann so aufrufe:
CSafeStdioFile inputfile; ... inputfile.Open(input, CFile::modeRead | CFile::shareDenyNone); // die Openfunktion hab ich noch reingepackt ... inputfile.ReadString(str); ...
aber irgendwas stimmt da noch nicht
er stürt gleich ab, "programm muss beendet werden"...