2013-07-30 10 views
8

Ho una classe nella DLL. E questa DLL fornisce una "interfaccia" per creare oggetti di questa classe e chiamare i loro metodi.DLL e classe nell'applicazione con multithreading

Codice classe (semplificato):

TLogger = class 
    private 
    // 
    public 
    function AddToLog(sBuf: PWideChar): Word; 
    constructor Create(Name: PWideChar); 
    destructor Destroy; override; 
end; 

constructor Create(Name: PWideChar); 
begin 
    // 
end; 

destructor TLogger.Destroy; 
begin 
    // 
end; 

function TLogger.AddToLog(sBuf: PWideChar): Word; 
var 
    Temp1 : WideString; 
    Temp2 : AnsiString; 

begin 
    Result := NO_ERRORS; 

    WaitForSingleObject(FMutex, INFINITE); 

    Temp1 := 'a'; 
    Temp2 := Temp1; 

    // 

    ReleaseMutex(FMutex); 
end; 

codice DLL

function CreateLogger(LogFileName: PWideChar; PLogger: PCardinal): Word; stdcall; 
begin 
    try 
    PLogger^ := Cardinal(TLogger.Create(LogFileName)); 
    Result := NO_ERRORS; 
    except 
    Result := ERR_CREATE_OBJECT; 
    end; 
end; 

function DestroyLogger(PLogger: Cardinal): Word; stdcall; 
begin 
    try 
    TLogger(PLogger).Free; 
    Result := NO_ERRORS; 
    except 
    Result := ERR_OBJECT_NOT_FOUND; 
    end; 
end; 

function AddToLog(PLogger: Cardinal; BufStr: PWideChar): Word; stdcall; 
begin 
    try 
    Result := TLogger(PLogger).AddToLog(BufStr); 
    except 
    Result := ERR_OBJECT_NOT_FOUND; 
    end; 
end; 

Quando sto cercando di usare questa libreria dal 1 filo - tutto è OK. I problemi iniziano quando creo molti thread che chiamano la funzione AddToLog con periodi casuali (ogni thread ha il proprio oggetto della classe). In un dato momento, prendo lo Access Violation o lo Invalid pointer operation. Ho fatto delle ricerche e ho sottolineato che se la variabile Temp2 ha il tipo WideString, tutto è OK. Un'altra soluzione è quella di spostare mutex al codice della libreria (è solo un codice di "ricerca"):

function AddToLog(PLogger: Cardinal; BufStr: PWideChar): Word; stdcall; 
begin 
    WaitForSingleObject(TLogger(PLogger).FMutex, INFINITE); 
    Result := TLogger(PLogger).AddToLog(BufStr); 
    ReleaseMutex(TLogger(PLogger).FMutex); 
end; 

La seconda soluzione è un male per me, perché ogni oggetto ha il proprio mutex (idea è che se due oggetti deve lavorare con un file, hanno lo stesso mutex che si aspettano a vicenda, se due oggetti devono lavorare con file diversi, hanno mutex diversi e funzionano in parallelo).

Sto provando a risolvere questo problema per 2 giorni ma non riesco a capire cosa vada storto. In che modo il cast di stringhe può causare questo problema?

+0

Mi sembra di aver rimosso troppo codice. Il codice che hai qui è thread-safe anche se rimuovi il mutex e anche se tutti i thread condividono la stessa istanza del logger. Hai bisogno di un SSCCE. –

+5

Imposta ['IsMultiThread'] (http://docwiki.embarcadero.com/Libraries/XE4/en/System.IsMultiThread): = True; nella DLL? –

+0

@TOndrej Ah, scommetto che è tutto. Con 'IsMultiThread' come' False' l'allocatore dell'heap non sarà protetto da thread. Suggerisco di aggiungerla come risposta. –

risposta

14

Mettere la seguente riga:

IsMultiThread := True; 

come prima linea nel blocco di codice principale del progetto DLL. Questo istruirà il gestore della memoria a passare a una modalità thread-safe.

Questo spiegherebbe la differenza di comportamento tra AnsiString e WideString perché AnsiString assegnata dal gestore di memoria Delphi, e WideString è allocato sul mucchio COM. Quando IsMultiThread è False, il gestore della memoria Delphi non è thread-safe, ma l'heap COM è sempre thread-safe.

+0

+1 Nota che WideString è thread-safe ma anche molto più lento di AnsiString o UnicodeString. Anche se la velocità degli allocatori di sistema BSTR è aumentata molto da Windows Vista, rispetto a XP o 2K. –

+0

Questo mi ha aiutato a risolvere un problema con cui ho avuto problemi per 3 giorni. Sto usando oggetti COM che possono chiamare i miei callback da thread diversi. Ho cercato strani errori relativi alla disposizione dell'array (de), all'interfaccia free e al conteggio dei riferimenti errati tutto intorno al codice a causa di misteriose condizioni di gara ... Ho davvero fatto non si sa che il gestore di memoria Delphi predefinito non è thread-safe a meno che non lo si abiliti. –