Thread Safe StringList

by admin on June 16, 2008

Hi Guys, it’s been a while since my last posting here. There are alot of responsibility now on my job. Recently  i got opportunity to play with threading again and this this time those multiple threading accesing one same TStringList.

for some of alldelphi.com user that has experienced on  multithreading field, i think already know that borland Delphi standard TStringlist is not a thread safe. let say that i have learned it the hardway i took several scratching on the head and slap on my forehand before i realiase that the TStringList is not a thread safe, so i need to figure out how to make the stringlist become a thread safe.Luckyly for me that a quick serach on google show this source .

So Guys , In Short here is the listing so may every body that come to alldelphi.com can now use TStringList safely on threading.


{-------------------------------------------------------------
  TThreadStringList
  Author: Tilo Eckert
  Date: 19.06.2004

  TThreadStringList is a simple wrapper for TStringList.
  This makes it possible to access a StringList from
  different threads without any conflicts.
  Most functions and properties are included.
 -------------------------------------------------------------
  TThreadStringList ist ein einfacher Wrapper für TStringList,
  der es ermöglicht von verschiedenen Threads auf eine
  Stringliste zuzugreifen ohne das Konflikte entstehen.
  Die wichtigsten Funktionen und Eigenschaften sind enthalten.
 -------------------------------------------------------------}

unit UThreadStringList;

interface

uses
  Classes, Windows;

type
  TThreadStringList = class
  private
    FStringList: TStringList;
    FLock: TRTLCriticalSection;
    function GetDuplicates: TDuplicates;
    procedure SetDuplicates(dup: TDuplicates);
    function GetCapacity: Integer;
    procedure SetCapacity(capa: Integer);
    function GetCommaText: string;
    procedure SetCommaText(const S: string);
    function GetCount: Integer;
    function GetDelimiter: Char;
    procedure SetDelimiter(delim: Char);
    function GetDelimitedText: string;
    procedure SetDelimitedText(const S: string);
    function GetNames(Index: Integer): string;
    function GetValues(const Name: string): string;
    procedure SetValues(const Name: string; S: string);
    function GetStrings(Index: Integer): string;
    procedure SetStrings(Index: Integer; S: string);
    function GetAsText: string;
    procedure SetAsText(S: string);
  public
    constructor Create;
    destructor Destroy; override;
    function LockList: TStringList;
    procedure UnlockList;
    function Add(const S: string): Integer;
    procedure AddStrings(Strings: TStrings);
    procedure Delete(Index: Integer);
    procedure Clear;
    procedure Exchange(Index1, Index2: Integer);
    function Find(const S: string; var Index: Integer): Boolean;
    procedure Insert(Index: Integer; const S: string);
    function IndexOf(const S: string): Integer;
    function IndexOfName(const Name: string): Integer;
    procedure Sort;
    function GetText: PAnsiChar;
    procedure LoadFromFile(const FileName: string);
    procedure LoadFromStream(Stream: TStream);
    procedure SaveToFile(const FileName: string);
    procedure SaveToStream(Stream: TStream);
    property Duplicates: TDuplicates read GetDuplicates write SetDuplicates;
    property Capacity: Integer read GetCapacity write SetCapacity;
    property CommaText: string read GetCommaText write SetCommaText;
    property Count: Integer read GetCount;
    property Delimiter: Char read GetDelimiter write SetDelimiter;
    property DelimitedText: string read GetDelimitedText write SetDelimitedText;
    property Names[Index: Integer]: string read GetNames;
    property Values[const Name: string]: string read GetValues write SetValues;
    property Strings[Index: Integer]: string read GetStrings write SetStrings; default;
    property Text: string read GetAsText write SetAsText;
  end;

implementation

{ TThreadStringList }

constructor TThreadStringList.Create;
begin
  inherited Create;
  InitializeCriticalSection(FLock);
  FStringList := TStringList.Create;
  FStringList.Duplicates := dupIgnore;
end;

destructor TThreadStringList.Destroy;
begin
  LockList;
  try
    FStringList.Free;
    inherited Destroy;
  finally
    UnlockList;
    DeleteCriticalSection(FLock);
  end;
end;

function TThreadStringList.LockList: TStringList;
begin
  EnterCriticalSection(FLock);
  Result := FStringList;
end;

procedure TThreadStringList.UnlockList;
begin
  LeaveCriticalSection(FLock);
end;

function TThreadStringList.Add(const S: string): Integer;
begin
  Result := -1;
  LockList;
  try
    Result := FStringList.Add(S);
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.AddStrings(Strings: TStrings);
begin
  LockList;
  try
    FStringList.AddStrings(Strings);
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.Delete(Index: Integer);
begin
  LockList;
  try
    FStringList.Delete(Index);
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.Clear;
begin
  LockList;
  try
    FStringList.Clear;
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.Exchange(Index1, Index2: Integer);
begin
  LockList;
  try
    FStringList.Exchange(Index1, Index2);
  finally
    UnlockList;
  end;
end;

function TThreadStringList.Find(const S: string; var Index: Integer): Boolean;
begin
  LockList;
  try
    Result := FStringList.Find(S, Index);
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.Insert(Index: Integer; const S: string);
begin
  LockList;
  try
    FStringList.Insert(Index, S);
  finally
    UnlockList;
  end;
end;

function TThreadStringList.IndexOf(const S: string): Integer;
begin
  Result := -1;
  LockList;
  try
    Result := FStringList.IndexOf(S);
  finally
    UnlockList;
  end;
end;

function TThreadStringList.IndexOfName(const Name: string): Integer;
begin
  Result := -1;
  LockList;
  try
    Result := FStringList.IndexOfName(Name);
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.Sort;
begin
  LockList;
  try
    FStringList.Sort;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetText: PAnsiChar;
begin
  LockList;
  try
    Result := FStringList.GetText;
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.LoadFromFile(const FileName: string);
begin
  LockList;
  try
    FStringList.LoadFromFile(FileName);
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.LoadFromStream(Stream: TStream);
begin
  LockList;
  try
    FStringList.LoadFromStream(Stream);
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SaveToFile(const FileName: string);
begin
  LockList;
  try
    FStringList.SaveToFile(FileName);
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SaveToStream(Stream: TStream);
begin
  LockList;
  try
    FStringList.SaveToStream(Stream);
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetDuplicates: TDuplicates;
begin
  LockList;
  try
    Result := FStringList.Duplicates;
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SetDuplicates(dup: TDuplicates);
begin
  LockList;
  try
    FStringList.Duplicates := dup;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetCapacity: Integer;
begin
  LockList;
  try
    Result := FStringList.Capacity;
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SetCapacity(capa: Integer);
begin
  LockList;
  try
    FStringList.Capacity := capa;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetCommaText: string;
begin
  LockList;
  try
    Result := FStringList.CommaText;
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SetCommaText(const S: string);
begin
  LockList;
  try
    FStringList.CommaText := S;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetCount: Integer;
begin
  LockList;
  try
    Result := FStringList.Count;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetDelimiter: Char;
begin
  LockList;
  try
    Result := FStringList.Delimiter;
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SetDelimiter(delim: Char);
begin
  LockList;
  try
    FStringList.Delimiter := delim;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetDelimitedText: string;
begin
  LockList;
  try
    Result := FStringList.DelimitedText;
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SetDelimitedText(const S: string);
begin
  LockList;
  try
    FStringList.DelimitedText := S;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetNames(Index: Integer): string;
begin
  LockList;
  try
    Result := FStringList.Names[Index];
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetValues(const Name: string): string;
begin
  LockList;
  try
    Result := FStringList.Values[Name];
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SetValues(const Name: string; S: string);
begin
  LockList;
  try
    FStringList.Values[Name] := S;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetStrings(Index: Integer): string;
begin
  LockList;
  try
    Result := FStringList.Strings[Index];
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SetStrings(Index: Integer; S: string);
begin
  LockList;
  try
    FStringList.Strings[Index] := S;
  finally
    UnlockList;
  end;
end;

function TThreadStringList.GetAsText: string;
begin
  LockList;
  try
    Result := FStringList.Text;
  finally
    UnlockList;
  end;
end;

procedure TThreadStringList.SetAsText(S: string);
begin
  LockList;
  try
    FStringList.Text := S;
  finally
    UnlockList;
  end;
end;

end.

{ 3 comments… read them below or add one }

rezor October 2, 2009 at 4:50 pm

Credits to this code were omitted:
TThreadStringList
Author: Tilo Eckert
Date: 19.06.2004

TThreadStringList is a simple wrapper for TStringList.
This makes it possible to access a StringList from
different threads without any conflicts.
Most functions and properties are included.
————————————————————-
TThreadStringList ist ein einfacher Wrapper für TStringList,
der es ermöglicht von verschiedenen Threads auf eine
Stringliste zuzugreifen ohne das Konflikte entstehen.
Die wichtigsten Funktionen und Eigenschaften sind enthalten.

Reply

admin October 5, 2009 at 11:02 am

@Rezor,

You are correct mate, for the author I’am sorry that I mistakenly omitted the the Credit for your name.

Credit have been added to the Source..

Thanx mate for you correction..

Reply

MvanZwijndregt September 22, 2010 at 6:13 pm

A word of caution regarding the destructor:

# destructor TThreadStringList.Destroy;
# begin
# LockList;
# try
# FStringList.Free;
# inherited Destroy;
# finally
# UnlockList;
# DeleteCriticalSection(FLock);
# end;
# end;

I’ve had problems when freeing objects after the inherited Destroy was called, like in this code example. (In my case running on linux, using FPC; http://community.freepascal.org:10000/bboards/message?message_id=391565&forum_id=24083)

If you run into an unobvious deadlock on the destructor code, you might want to try and move out the inherited Destroy to the bottom of the destructor.

Reply

Leave a Comment

Previous post:

Next post: