2013-05-07 19 views
4

在此代碼:TThread線程安全嗎?

TMyClass = class(TThread) 
public 
    FInputBuffer  : TThreadedQueue<TBytes>; 
protected 
    procedure Execute; override; 
end; 

是否使用(在TMyClass和其他類)FInputBuffer的是線程安全的?

編輯:

樣品用途:TMyClass:

procedure TMyClass.Execute; 
var x  :TBytes; 
begin 
    inherited; 
    FInputBuffer:= TThreadedQueue<TBytes>.Create; 
    while not Terminated do begin 
    if FInputBuffer.QueueSize > 0 then begin 
     x:= FInputBuffer.PopItem; 
     //some code to use x 
    end; 
    end; 
    FInputBuffer.Free; 
end; 

在其他類:

var MyClass :TMyClass ; 

procedure TForm1.btn1Click(Sender: TObject); 
var x :TBytes; 
begin 
    //set x 
    MyClass.FInputBuffer.PushItem(x); 
end; 
+0

1.您必須定義線程安全的含義。有很多方法可以解釋這個術語。 2.即使我們知道您的線程安全意味着什麼,您也沒有顯示您的其他代碼,但我們很難判斷其他代碼是否符合該要求。 – 2013-05-07 18:20:20

+1

對我來說一個規則:公共領域是**從不**線程安全,因爲公共寫入訪問 – 2013-05-07 21:15:29

回答

11

如果FInputBuffer是線程之前在線程構造創建開始運行,並且在線程析構函數中釋放了線程運行完畢後,是的,從其他線程訪問它是線程安全的,而TMyClass對象仍處於活動狀態,因爲TThreadedQueue爲其內部內容提供了其自己的線程安全性。如果MyClass變量在調用btn1Click()時有效,那麼顯示的是多線程隊列的完全有效使用。

然而,如果FInputBufferExecute()內部創建,那麼它是不是線程安全的,因爲btn1Click()可能會嘗試訪問該隊列的線程已開始運行之前,已創建FInputBuffer之前。這就是爲什麼你需要創建在構造函數中FInputBuffer代替,例如:

TMyClass = class(TThread) 
public 
    FInputBuffer: TThreadedQueue<TBytes>; 
    constructor Create(ACreateSuspended: Boolean); override; 
    destructor Destroy; override; 
protected 
    procedure Execute; override; 
end; 

constructor TMyClass.Create(ACreateSuspended: Boolean); 
begin 
    inherited; 
    FInputBuffer := TThreadedQueue<TBytes>.Create; 
end; 

destructor TMyClass.Destroy; 
begin 
    FInputBuffer.Free; 
    inherited; 
end; 

procedure TMyClass.Execute; 
var 
    x: TBytes; 
begin 
    while not Terminated do begin 
    if FInputBuffer.QueueSize > 0 then begin 
     x := FInputBuffer.PopItem; 
     // some code to use x 
    end; 
    end; 
end; 

如果你想創建FInputBufferExecute()內,那麼線程應當暴露那些被實際上已經創造了FInputBuffer後設置的標誌/信號,然後沒有其他代碼應該嘗試訪問FInputBuffer,直到該標誌/信號已被設置。創建線程實例應該等待標誌/信號控制權返回到代碼的其餘部分之前的代碼,例如:

TMyClass = class(TThread) 
public 
    FInputBuffer: TThreadedQueue<TBytes>; 
    FInputBufferCreated: TEvent; 
    constructor Create(ACreateSuspended: Boolean); override; 
    destructor Destroy; override; 
protected 
    procedure Execute; override; 
    procedure DoTerminate; override; 
end; 

constructor TMyClass.Create(ACreateSuspended: Boolean); 
begin 
    inherited; 
    FInputBufferCreated := TEvent.Create(nil, True, False, ''); 
end; 

destructor TMyClass.Destroy; 
begin 
    FInputBufferCreated.Free; 
    inherited; 
end; 

procedure TMyClass.Execute; 
var 
    x: TBytes; 
begin 
    FInputBuffer := TThreadedQueue<TBytes>.Create; 
    FInputBufferCreated.SetEvent; 

    while not Terminated do begin 
    if FInputBuffer.QueueSize > 0 then begin 
     x := FInputBuffer.PopItem; 
     // some code to use x 
    end; 
    end; 
end; 

procedure TMyClass.DoTerminate; 
begin 
    if FInputBufferCreated <> nil then 
    FInputBufferCreated.ResetEvent; 
    FreeAndNil(FInputBuffer); 
    inherited; 
end; 

var 
    MyClass: TMyClass = nil; 

procedure TForm1.StartBufferThread; 
var 
    I: Integer; 
begin 
    MyClass := TMyClass.Create(False); 
    if MyClass.FInputBufferCreated.WaitFor(2500) <> wrSignaled then 
    begin 
    MyClass.Terminate; 
    MyClass.WaitFor; 
    FreeAndNil(MyClass); 
    raise Exception.Create('MyClass.FInputBuffer not created after 2.5 seconds!'); 
    end; 
end; 

procedure TForm1.btn1Click(Sender: TObject); 
var 
    x: TBytes; 
begin 
    //set x 
    if MyClass <> nil then 
    MyClass.FInputBuffer.PushItem(x); 
end; 
+1

哇........(15) – 2013-05-07 19:57:40