2011-05-01 15 views
10

Questo codicesono `parametri di stringa const` (thread) sicuro

procedure MyThreadTestA(const AStr: string); 

è più veloce di

procedure MyThreadTestB(AStr: string); 

mentre facendo lo stesso lavoro, sia passare un puntatore.

Tuttavia, la versione B 'correttamente' aggiorna il riferimento di AStr e fa una copia se la cambio.
Versione A passa solo un puntatore e solo il compilatore mi impedisce di modificare AStr.

Versione A non è sicuro se faccio trucchi sporchi in Assembler o in altro modo per aggirare la protezione compilatore, questo è ben noto, ma ...

viene passato AStr per riferimento come un const parametri thread-safe?
Cosa succede se il conteggio di riferimento di AStr in qualche altro thread si azzera e la stringa viene distrutta?

+3

Se il conteggio dei riferimenti va a zero in un altro thread, il conteggio dei riferimenti era sbagliato per cominciare. Se due pezzi di codice possono entrambi modificare la stessa stringa, il conteggio dei riferimenti della stringa deve essere maggiore di 1 perché esistono chiaramente diversi modi per fare riferimento a quella stringa. Ogni thread deve avere una propria variabile indipendente per l'arbitraggio della stringa, oppure la variabile condivisa deve essere protetta con le consuete tecniche di sincronizzazione. –

+0

Ottima domanda. Ho imparato qualcosa oggi. –

risposta

16

No, tali trucchi non sono thread-safe. Const impedisce l'aggiunta-ref, quindi le modifiche apportate da un altro thread influenzeranno il valore in modi imprevedibili. Programma di esempio, provare a modificare le const nella definizione di P:

{$apptype console} 
uses SysUtils, Classes, SyncObjs; 

type 
    TObj = class 
    public 
    S: string; 
    end; 

    TWorker = class(TThread) 
    public 
    procedure Execute; override; 
    end; 

var 
    lock: TCriticalSection; 
    obj: TObj; 

procedure P(const x: string); 
// procedure P(x: string); 
begin 
    Writeln('P(1): x = ', x); 
    Writeln('Releasing obj'); 
    lock.Release; 
    Sleep(10); // give worker a chance to run 
    Writeln('P(2): x = ', x); 
end; 

procedure TWorker.Execute; 
begin 
    // wait until TMonitor is freed up 
    Writeln('Worker started...'); 
    lock.Acquire; 
    Writeln('worker fiddling with obj.S'); 
    obj.S := 'bar'; 
    TMonitor.Exit(obj); 
end; 

procedure Go; 
begin 
    lock := TCriticalSection.Create; 
    obj := TObj.Create; 
    obj.S := 'foo'; 
    UniqueString(obj.S); 
    lock.Acquire; 
    TWorker.Create(False); 
    Sleep(10); // give worker a chance to run and block 
    P(obj.S); 
end; 

begin 
    Go; 
end. 

Ma non è solo limitato a discussioni; modificando la posizione variabile sottostante ha effetti simili:

{$apptype console} 
uses SysUtils, Classes, SyncObjs; 

type 
    TObj = class 
    public 
    S: string; 
    end; 

var 
    obj: TObj; 

procedure P(const x: string); 
begin 
    Writeln('P(1): x = ', x); 
    obj.S := 'bar'; 
    Writeln('P(2): x = ', x); 
end; 

procedure Go; 
begin 
    obj := TObj.Create; 
    obj.S := 'foo'; 
    UniqueString(obj.S); 
    P(obj.S); 
end; 

begin 
    Go; 
end. 
4

Per aggiungere alla risposta di Barry: E 'sicuramente thread-safe se la stringa che ha ottenuto passata è venuto da una variabile locale all'interno del campo di applicazione chiamanti.

In tal caso quella variabile locale avrà un riferimento valido e l'unico modo (assumendo solo un codice pascal valido, senza manipolare in giro in asm) per quella variabile locale da modificare è se la chiamata ritorna.

Ciò include anche tutti i casi in cui l'origine della variabile stringa è il risultato di una chiamata di funzione (incluso l'accesso alla proprietà, ad esempio TStrings.Strings []) perché in questo caso il compilatore deve memorizzare la stringa in un temp locale variabile.

I problemi di sicurezza dei thread possono verificarsi solo se si passa direttamente una stringa da una posizione in cui tale stringa può essere modificata (dallo stesso o da un altro thread) prima che la chiamata ritorni.

+0

? se la stringa var è una variabile locale var allora sì che var locale è thread-safe durante la chiamata, ma l'origine effettiva di tale var può ancora essere stata modificata da un altro thread quando si chiama return ... Ora, ciò può essere esattamente come lo vuoi tu, ma se vuoi assicurarti che la stringa originale rimanga la stessa mentre chiami la tua funzione, dovresti usare un qualche tipo di blocco. –

+0

La domanda riguardava il passaggio di una stringa con const e senza di essa e le implicazioni per la sicurezza del thread dell'accesso a quella stringa all'interno del metodo chiamato. La mia risposta è ovviamente * nel contesto della domanda originale *. Quello di cui stai parlando è qualcosa di completamente diverso che non ha nulla a che fare con la domanda originale o la mia risposta. –

+0

Nessuna discussione a riguardo. Tuttavia esiste una distinzione molto reale tra una stringa variabile locale e una variabile temporanea con un'origine al di fuori del contesto locale. Ho commentato perché meno sviluppatori esperti di multi-thread potrebbero non capirlo e potrebbero pensare che passare una stringa [i] come/a un argomento const sia "completamente" thread-safe. –

Problemi correlati