2013-07-30 14 views
8

Mam klasę wewnątrz mojej biblioteki DLL. I ta biblioteka DLL zapewnia "interfejs" do tworzenia obiektów tej klasy i wywoływania ich metod.DLL i klasa w aplikacji wielowątkowej

kod klasy (uproszczony):

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; 

kod 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; 

Kiedy staram się korzystać z tej biblioteki z 1 gwintem - wszystko jest OK. Problemy zaczynają się, gdy tworzę wiele wątków, które wywołują funkcję AddToLog z przypadkowymi okresami (każdy wątek ma własny obiekt klasy). W pewnym momencie łapię Access Violation lub Invalid pointer operation. Zrobiłem kilka badań i wskazałem, że jeśli zmienna Temp2 ma typ WideString, wszystko jest w porządku. Innym rozwiązaniem jest przeniesienie mutex do kodu biblioteki (to tylko „badania” code):

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

Drugie rozwiązanie jest złe dla mnie, ponieważ każdy obiekt ma własną mutex (pomysł jest, że jeśli dwa obiekty muszą pracować z jednym plikiem mają ten sam muteks, aby czekać jeden drugiego, jeśli dwa obiekty muszą pracować z różnymi plikami, mają różne muteksy i działają równolegle).

Próbuję rozwiązać ten problem przez 2 dni, ale nie mogę zrozumieć, co jest nie tak. Jak odlewanie ciągów może powodować taki problem?

+0

Wydaje mi się, że usunięto zbyt wiele kodu. Kod, który tu masz, jest bezpieczny dla wątków, nawet jeśli usuniesz muteks, a nawet jeśli wszystkie wątki współużytkują to samo wystąpienie rejestratora. Potrzebujesz SSCCE. –

+5

Czy ustawić ['IsMultiThread'] (http://docwiki.embarcadero.com/Libraries/XE4/en/System.IsMultiThread): = True; w bibliotece DLL? –

+0

@TOndrej Ah, założę się, że to wszystko. Z 'IsMultiThread' jako' False' alokator sterty nie będzie bezpieczny dla wątków. Proponuję dodać to jako odpowiedź. –

Odpowiedz

14

Umieść następujący wiersz:

IsMultiThread := True; 

jako pierwsza linia w głównym bloku kodu Twojego projektu DLL. Spowoduje to, że menedżer pamięci przejdzie w tryb wątku.

To wyjaśniałoby różnicy między AnsiString zachowań i WideString ponieważ AnsiString jest alokowana przez zarządcę pamięci Delphi i WideString jest alokowany na stercie COM. Gdy IsMultiThread jest False, menedżer pamięci Delphi nie jest bezpieczny dla wątków, ale stos COM jest zawsze bezpieczny dla wątków.

+0

+1 Należy również zauważyć, że WideString jest bezpieczny dla wątków, ale także znacznie wolniejszy niż AnsiString lub UnicodeString. Nawet jeśli szybkość przydzielania systemu BSTR wzrósł znacznie od czasu Windows Vista, w stosunku do XP lub 2K. –

+0

Pomogło mi to rozwiązać problem, z którym borykałem się przez 3 dni. Używam obiektów COM, które mogą wywoływać moje wywołania zwrotne z różnych wątków. Ścigałem dziwne błędy związane z przydzielaniem tablic (de), uwalnianiem interfejsu i złą liczbą odnośników w całym kodzie z powodu tajemniczych warunków wyścigu ... Naprawdę to zrobiłem nie wiesz, że domyślny menedżer pamięci Delphi nie jest wątkowo bezpieczny, dopóki go nie włączysz. –

Powiązane problemy