Короче повозился с отладчиком, прошёлся по тексту и понял, что метод Canvas.Lock(), видимо, не отрабатывает до конца, т.е. не блокирует обращение к свойствам  Canvas (конкретно к объекту Canvas.Pen) из других потоков.
Ошибка возникает при установке цвета пера в потоке:
- Код: Выделить всё
-         with FForm.Canvas do
 begin
 Lock;
 Pen.Color:= FColor; // <-- вызов приводящий к исключению
 MoveTo(P1);
 LineTo(P2);
 Unlock;
 Sleep(1);
 end;
 
дальше вызов переходит к методу Pen.SetColor:
- Код: Выделить всё
- procedure TPen.SetColor(const NewColor: TColor; const NewFPColor: TFPColor);
 begin
 if (NewColor = Color) and (NewFPColor = FPColor) then Exit; // если цвет не изменился то выходим процедуры
 FreeReference; // <-- а если изменился, то удаляем ссылку на существующее перо из кэша ресурсов?
 FColor := NewColor;
 inherited SetFPColor(NewFPColor);
 Changed;
 end;
 
 дальше вызывается Pen.FreeReference:
- Код: Выделить всё
- procedure TPen.FreeReference;
 begin
 if not FReference.Allocated then Exit;
 
 Changing;
 if FPenHandleCached then
 begin
 PenResourceCache.FindPen(FReference.Handle).DecreaseRefCount; //<-- здесь вызывается исключение т.к. "кто-то" уже удалил из кэша ресурсов предыдущие перо и RefCount = 0.
 FPenHandleCached := False;
 end else
 DeleteObject(HGDIOBJ(FReference.Handle));
 FReference._lclHandle := 0;
 end;
 
Думаю, что "кто-то" - это основной поток выполнения (GUI). После того как Pen "закэшировался", происходит переключение между потоками в момент после вызова TPen.FreeReference, но до вызова PenResourceCache.FindPen(FReference.Handle).DecreaseRefCount; и следующий поток несмотря на canvas.lock() получает доступ к canvas.pen и выполняет DecreaseRefCount раньше прерванного потока.
Добавлено спустя 8 минут 16 секунд:В связи с этим сделал вызов отрисовки на Canvas в синхронизированном методе (т.е. в основном потоке выполнения приложения) - и теперь проблема исчезла! Таким образом это косвенно подтверждает что именно главный поток управления не обращал внимание на Canvas.lock().
Вот изменённый код TDrawThread из main.pas (всё остальное осталось без изменений):
- Код: Выделить всё
- TDrawThread = class(TThread)
 private
 P1, P2: TPoint;
 FColor: TColor;
 FForm:  TForm;
 procedure DrawLine;
 procedure GetRandCoords;
 public
 constructor Create(AForm: TForm; AColor: TColor);
 procedure Execute; override;
 end;
 
 procedure TDrawThread.DrawLine;
 begin
 with FForm.Canvas do
 begin
 Pen.Color:= FColor;
 MoveTo(P1);
 LineTo(P2);
 end;
 end;
 procedure TDrawThread.Execute;
 begin
 //FreeOnTerminate:= True;
 while not (self.Terminated or Application.Terminated) do
 begin
 Synchronize(@GetRandCoords);
 Synchronize(@DrawLine);
 Sleep(1);
 end;
 self.Free;
 self:=  nil;
 end;
 
Добавлено спустя 1 час 37 минут 49 секунд:ещё немного подумал и понял, что в таком варианте - приложение по факту однопоточное, т.к. параллельно потоки только "спят" 

немного изменил приложение, так, чтоб в разных потоках, хотя бы координаты для линий параллельно "считались".:
- Код: Выделить всё
- unit Main;
 
 {$mode objfpc}{$H+}
 
 interface
 
 uses
 Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
 Menus;
 
 type
 
 { TMainForm }
 
 TMainForm = class(TForm)
 ColorDialog1: TColorDialog;
 MainMenu1: TMainMenu;
 AddThread: TMenuItem;
 Add10: TMenuItem;
 RemoveAll: TMenuItem;
 RemoveThread: TMenuItem;
 Options1: TMenuItem;
 procedure Add10Click(Sender: TObject);
 procedure AddThreadClick(Sender: TObject);
 procedure FormCreate(Sender: TObject);
 procedure FormDestroy(Sender: TObject);
 procedure RemoveAllClick(Sender: TObject);
 procedure RemoveThreadClick(Sender: TObject);
 private
 { private declarations }
 ThreadList: TList;
 public
 { public declarations }
 end;
 
 { TDrawThread }
 
 TDrawThread = class(TThread)
 private
 P1, P2: TPoint;
 FColor: TColor;
 FForm:  TForm;
 MaxX, MaxY: Integer;
 procedure DrawLine;
 procedure GetFormCoords;
 public
 constructor Create(AForm: TForm; AColor: TColor);
 procedure Execute; override;
 end;
 
 var
 MainForm: TMainForm;
 
 implementation
 
 { TMainForm }
 
 procedure TMainForm.FormCreate(Sender: TObject);
 begin
 ThreadList:= TList.Create;
 self.Caption:= Format('Num. of Treads [%u]',[ThreadList.Count]);
 end;
 
 procedure TMainForm.AddThreadClick(Sender: TObject);
 begin
 if ColorDialog1.Execute then
 ThreadList.Add(TDrawThread.Create(Self, ColorDialog1.Color));
 self.Caption:= Format('Num. of Treads [%u]',[ThreadList.Count]);
 end;
 
 procedure TMainForm.Add10Click(Sender: TObject);
 var
 i: integer;
 begin
 for i:= 1 to 10 do
 ThreadList.Add(TDrawThread.Create(Self, Random(MaxInt)));
 self.Caption:= Format('Num. of Treads [%u]',[ThreadList.Count]);
 end;
 
 procedure TMainForm.FormDestroy(Sender: TObject);
 begin
 RemoveAllClick(nil);
 ThreadList.Free;
 end;
 
 procedure TMainForm.RemoveAllClick(Sender: TObject);
 var
 i: integer;
 begin
 Cursor:= crHourGlass;
 try
 for i:= ThreadList.Count - 1 downto 0 do
 begin
 TDrawThread(ThreadList[i]).Terminate;
 //            TDrawThread(ThreadList[i]).WaitFor;
 end;
 ThreadList.Clear;
 finally
 Cursor:= crDefault;
 end;
 self.Caption:= Format('Num. of Treads [%u]',[ThreadList.Count]);
 end;
 
 procedure TMainForm.RemoveThreadClick(Sender: TObject);
 begin
 if ThreadList.Count < 1 then exit;
 TDrawThread(ThreadList[ThreadList.Count - 1]).Terminate;
 ThreadList.Delete(ThreadList.Count - 1);
 self.Caption:= Format('Num. of Treads [%u]',[ThreadList.Count]);
 end;
 
 { TDrawThread }
 
 constructor TDrawThread.Create(AForm: TForm; AColor: TColor);
 begin
 FColor:= AColor;
 FForm := AForm;
 inherited Create(False);
 end;
 
 procedure TDrawThread.GetFormCoords;
 
 begin
 
 self.MaxX:= FForm.ClientWidth;
 self.MaxY:= FForm.ClientHeight;
 
 end;
 
 procedure TDrawThread.DrawLine;
 begin
 with FForm.Canvas do
 begin
 Pen.Color:= FColor;
 MoveTo(P1);
 LineTo(P2);
 end;
 end;
 
 procedure TDrawThread.Execute;
 
 begin
 FreeOnTerminate:= True;
 while not (self.Terminated or Application.Terminated) do
 begin
 Synchronize(@GetFormCoords);
 
 P1.x:= Random(self.MaxX);
 P2.x:= Random(self.MaxX);
 P1.y:= Random(self.MaxY);
 P2.y:= Random(self.MaxY);
 
 Synchronize(@DrawLine);
 Sleep(1);
 end;
 end;
 
 initialization
 {$I main.lrs}
 Randomize;
 
 end.
 
и на счёт что лучше для лазаруса: 
- Код: Выделить всё
- FreeOnTerminate:=True;
 
или
- Код: Выделить всё
- Free;             
 Self:=Nil;
 
при втором варианте в windows XP 32b при удалении потоков в приложении - в диспетчере задач количество потоков в процессе не уменьшается! в первом же случае всё корректно.
Чтобы посмотреть количество потоков в приложении нужно в диспетчере задач win xp  зайти и выбрать View->Select Coluns -> Thread Count. после этого на вкладке Processes появится дополнительный столбец Threads.
Так что чтобы не допускать утечек ресурсов системы я думаю лучше использовать вариант с  FreeOnTerminate:=True;