2011-05-01 25 views
10

Mã nàyAre 'thông số chuỗi const` (thread) an toàn

procedure MyThreadTestA(const AStr: string); 

là nhanh hơn so với

procedure MyThreadTestB(AStr: string); 

Trong khi làm cùng một công việc, cả hai vượt qua một con trỏ.

Tuy nhiên phiên bản B 'đúng' cập nhật referencecount của AStr và tạo một bản sao nếu tôi thay đổi nó.
Phiên bản A qua chỉ là một con trỏ và chỉ trình biên dịch ngăn cản tôi từ việc thay đổi AStr.

Version A là không an toàn nếu tôi làm thủ đoạn bẩn trong Assembler hoặc để phá vỡ sự bảo vệ biên dịch, điều này được nổi tiếng nhưng ...

là thông qua AStr bằng cách tham chiếu như một const thông số chủ đề an toàn?
gì xảy ra nếu tính tham khảo AStr 's trong một số chủ đề khác đi về không và chuỗi bị phá hủy?

+3

Nếu số tài liệu tham khảo đi đến số không trong chủ đề khác, sau đó số lượng tài liệu tham khảo đã sai để bắt đầu với. Nếu hai phần mã có thể sửa đổi cùng một chuỗi, thì số tham chiếu của chuỗi phải lớn hơn 1 vì có nhiều cách rõ ràng để tham chiếu đến chuỗi đó. Mỗi luồng phải có biến độc lập riêng của nó để gán trọng số cho chuỗi, nếu không biến chia sẻ phải được bảo vệ bằng các kỹ thuật đồng bộ hóa thông thường. –

+0

Câu hỏi rất hay. Tôi đã học được điều gì đó ngày hôm nay. –

Trả lời

16

Không, thủ đoạn như vậy không phải là thread-safe. Const ngăn chặn các add-ref, vì vậy thay đổi bởi thread khác sẽ ảnh hưởng đến giá trị theo những cách không thể đoán trước. chương trình mẫu, hãy thử thay đổi const trong định nghĩa của 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. 

Nhưng nó không chỉ giới hạn trong đề; sửa đổi vị trí biến cơ bản có tác dụng tương tự:

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

Để thêm vào câu trả lời của Barry: Nó chắc chắn là thread-safe nếu chuỗi đó đã trôi qua đến từ một biến cục bộ bên trong phạm vi người gọi.

Trong trường hợp đó biến cục bộ sẽ giữ tham chiếu hợp lệ và cách duy nhất (giả sử chỉ là mã pascal hợp lệ, không có xung quanh trong asm) cho biến cục bộ được thay đổi là khi cuộc gọi của bạn trả về.

Điều này cũng bao gồm tất cả các trường hợp nguồn của biến chuỗi là kết quả của một cuộc gọi hàm (bao gồm quyền truy cập thuộc tính, ví dụ: TStrings.Strings []) vì trong trường hợp này trình biên dịch phải lưu chuỗi trong một cục bộ cục bộ biến.

Vấn đề an toàn chủ đề chỉ có thể xảy ra nếu bạn đang trực tiếp truyền một chuỗi từ vị trí mà chuỗi đó có thể được thay đổi (bởi cùng một chủ đề khác) trước khi trả về cuộc gọi của bạn.

+0

? nếu chuỗi var là một biến cục bộ var thì yes rằng biến cục bộ var là thread an toàn trong suốt cuộc gọi của bạn, nhưng nguồn gốc thực sự của temp đó vẫn có thể bị thay đổi bởi một thread khác khi bạn gọi return ... chính xác như bạn muốn, nhưng nếu bạn muốn đảm bảo rằng chuỗi gốc sẽ vẫn giữ nguyên trong khi bạn gọi hàm, bạn nên sử dụng một số loại khóa. –

+0

Câu hỏi đặt ra là truyền một chuỗi với const và không có nó và các tác động đối với luồng an toàn của truy cập vào chuỗi đó bên trong phương thức được gọi. Câu trả lời của tôi rõ ràng là * trong ngữ cảnh của câu hỏi gốc *. Những gì bạn đang nói về là một cái gì đó hoàn toàn khác nhau mà không có gì để làm với câu hỏi ban đầu hoặc câu trả lời của tôi. –

+0

Không tranh luận về điều đó. Tuy nhiên có một sự khác biệt rất thực sự giữa một chuỗi biến cục bộ và một biến tạm thời với một nguồn gốc bên ngoài ngữ cảnh cục bộ. Tôi nhận xét bởi vì các nhà phát triển hiểu biết nhiều chủ đề ít có thể không nhận được trên đó và có thể đi xa suy nghĩ rằng đi qua một chuỗi [i] như/đến một đối số const là "hoàn toàn" thread-an toàn. –

Các vấn đề liên quan