2011-05-01 7 views
10
segura

Este códigoson `parámetros de cadena const` (rosca)

procedure MyThreadTestA(const AStr: string); 

es más rápido que

procedure MyThreadTestB(AStr: string); 

mientras se realiza el mismo trabajo, tanto pasar un puntero.

Sin embargo, la versión B 'correctamente' actualiza la cuenta de referencia de AStr y hace una copia si la cambio.
La versión A solo pasa un puntero y solo el compilador me impide cambiar AStr.

Versión A no es seguro si no hago trucos sucios en ensamblador o de otra manera de eludir la protección del compilador, esto es bien conocido, pero ...

se pasa por referencia AStr como un const parámetros hilo de seguridad?
¿Qué sucede si el recuento de referencias de AStr en otro hilo pasa a cero y la cadena se destruye?

+3

Si la cuenta de referencia va a cero en otra secuencia, entonces la cuenta de referencia fue incorrecta para comenzar. Si dos piezas de código pueden modificar la misma cadena, entonces el recuento de referencia de la cadena debería ser mayor que 1 porque hay claramente múltiples formas de referirse a esa cadena. Cada hilo debe tener su propia variable independiente para el arbitraje de la cadena, o bien la variable compartida debe protegerse con las técnicas de sincronización habituales. –

+0

Muy buena pregunta. Aprendí algo hoy. –

Respuesta

16

No, tales trucos no son seguros para subprocesos. Const previene el add-ref, por lo que los cambios por otro hilo afectarán el valor de maneras impredecibles. Programa de ejemplo, intenta modificar la const en la definición de 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. 

Pero no se limita sólo a las discusiones; la modificación de la dirección de la variable subyacente tiene efectos similares:

{$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

Para añadir a la respuesta de Barry: Es definitivamente flujos seguros si la cadena que conseguir pasado venía de una variable local dentro del ámbito que llaman.

En ese caso, esa variable local contendrá una referencia válida y la única forma (suponiendo solo un código pascal válido, sin manipular en asm) para que esa variable local sea modificada es si su llamada regresa.

Esto también incluye todos los casos donde la fuente de la variable de cadena es el resultado de una llamada de función (incluyendo acceso de propiedad, por ejemplo, TStrings.Strings []) porque en este caso el compilador tiene que almacenar la cadena en una temperatura local variable.

Los problemas de seguridad de subprocesos solo pueden producirse si está transfiriendo directamente una cadena desde una ubicación donde esa cadena puede cambiarse (por el mismo hilo u otro) antes de que regrese su llamada.

+0

? Si la cadena var es una var temporal local, entonces sí, la var temporal local es segura para la ejecución de subprocesos durante la llamada, pero el origen real de esa var temporal puede haber sido cambiado por otra cadena cuando se llama a return ... Ahora bien, eso puede ser exactamente como lo desee, pero si quiere asegurarse de que la cadena original permanezca igual mientras llama a su función, debe emplear algún tipo de bloqueo. –

+0

La pregunta era sobre pasar una cadena con const y sin ella y las implicaciones para la seguridad del hilo de acceso a esa cadena dentro del método llamado. Mi respuesta es obviamente * en el contexto de la pregunta original *. De lo que estás hablando es algo completamente diferente que no tiene nada que ver con la pregunta original o mi respuesta. –

+0

No hay argumento al respecto. Sin embargo, hay una distinción muy real entre una cadena variable local y una variable temporal con un origen fuera del contexto local. Comenté porque es posible que los desarrolladores menos expertos en múltiples subprocesos no se den cuenta de esto y puedan pensar que pasar un argumento Strings [i] as/to const es "completamente" seguro para subprocesos. –

Cuestiones relacionadas