为什么不执行线程代码?

时间:2014-03-08 10:41:56

标签: multithreading delphi thread-safety delphi-xe2

我在运行时创建了4个线程。每个线程进入临界区,更改全局变量,退出关键部分并显示带有结果的消息对话框。 OnThreadTerminate我还有一个消息对话框。它似乎是随机的,但是,我有时会得到3条带有结果的消息,还有一条说该线程被终止。怎么可能呢? Win7 x64。enter image description here 有我的完整代码:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ComCtrls,
  IdThreadComponent, idHTTP, SyncObjs;

const
  THREAD_NAME = 'MyidThreadComponent';

type
  TForm1 = class(TForm)
    StatusBar1: TStatusBar;
    BitBtn1: TBitBtn;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private declarations }
    FCriticalSection: TCriticalSection;
    FGlobalVariable: integer;
    procedure CreateThreads(const ACount: integer; const AStart: boolean);
    function GetWebsiteContent(const AURL: string): string;
    procedure MyIdThreadComponentOnRunHandler(Sender: TIdThreadComponent);
    procedure MyIdThreadComponentOnTerminateHandler(Sender: TIdThreadComponent);
  public
    { Public declarations }
    property GlobalVariable: integer read FGlobalVariable write FGlobalVariable;
    property CriticalSection: TCriticalSection read FCriticalSection;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FCriticalSection := TCriticalSection.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FCriticalSection);
end;

function TForm1.GetWebsiteContent(const AURL: string): string;
var
  _MyidHTTP: TidHTTP;
begin
  _MyidHTTP := TidHTTP.Create(self);
  try
    Result := _MyidHTTP.Get(AURL);
  finally
    FreeAndNil(_MyidHTTP);
  end;
end;

procedure TForm1.MyIdThreadComponentOnRunHandler(Sender: TIdThreadComponent);
var
  _LocalVariable: integer;
begin
  CriticalSection.Acquire;
  try
    // Safe way to deal with global variables. Only one thread will enter
    // CriticalSection at time.
    _LocalVariable := GlobalVariable;
    _LocalVariable := _LocalVariable * 2;
    GlobalVariable := _LocalVariable;
  finally
    CriticalSection.Release;
  end;
  ShowMessage(Sender.Name + ' started: ' + IntToStr(_LocalVariable));
  Sender.Terminate;
end;

procedure TForm1.MyIdThreadComponentOnTerminateHandler
  (Sender: TIdThreadComponent);
begin
  ShowMessage(Sender.Name + ' terminated.');
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  GlobalVariable := 1;
  CreateThreads(4 { System.CPUCount + 1 } , true);
end;

procedure TForm1.CreateThreads(const ACount: integer; const AStart: boolean);
var
  _MyIdThreadComponent: TIdThreadComponent;
  i: integer;
begin
  if ACount > 0 then
    for i := 1 to ACount do
    begin
      _MyIdThreadComponent := FindComponent(THREAD_NAME + IntToStr(i))
        as TIdThreadComponent;
      if not Assigned(_MyIdThreadComponent) then
      begin
        _MyIdThreadComponent := TIdThreadComponent.Create(self);
        _MyIdThreadComponent.Name := THREAD_NAME + IntToStr(i);
        _MyIdThreadComponent.Tag := i;
        _MyIdThreadComponent.OnRun := MyIdThreadComponentOnRunHandler;
        _MyIdThreadComponent.OnTerminate :=
          MyIdThreadComponentOnTerminateHandler;
{$IFDEF MSWINDOWS}
        _MyIdThreadComponent.Priority := tpNormal;
{$ENDIF}
{$IFDEF MACOS}
        _MyIdThreadComponent.Priority := 1;
{$ENDIF}
      end;
      if AStart = true then
        if Assigned(_MyIdThreadComponent) then
          _MyIdThreadComponent.Start;
    end;
end;

end.

1 个答案:

答案 0 :(得分:2)

Showmessage不是将输出显示为非线程安全的最佳方式。相反,如果您使用备忘录或其他控件并将其包装在同步调用中,则更容易看到结果。我修改了你的例程输出到备忘录,并在同步调用之前和之内包含了ThreadId,这样你就可以更好地理解发生了什么。

请记住,您的线程并不总是按照您认为的顺序输出,线程4完全有可能在线程1之前输出,即使线程1首先启动而最后4个启动。

procedure TForm13.MyIdThreadComponentOnRunHandler(Sender: TIdThreadComponent);
var
  _LocalVariable: integer;
  _LocalThreadId : Cardinal;
begin
  fCriticalSection.Acquire;
  try
    // Safe way to deal with global variables. Only one thread will enter
    // CriticalSection at time.
    _LocalVariable := GlobalVariable;
    _LocalVariable := _LocalVariable * 2;
    GlobalVariable := _LocalVariable;
  finally
    fCriticalSection.Release;
  end;
  _LocalThreadId := TThread.CurrentThread.ThreadID;
  TThread.Synchronize(TThread.CurrentThread,procedure begin
    memo1.Lines.Add(Format('%s Started (%d/%d): %d',[Sender.Name,_LocalThreadId,TThread.CurrentThread.ThreadID,_LocalVariable]));
  end);
  Sender.Terminate;
end;

procedure TForm13.MyIdThreadComponentOnTerminateHandler
  (Sender: TIdThreadComponent);
begin
  // note sync call is not needed as this is executed in the context of the main thread.
  memo1.Lines.Add(Format('%s terminated. (%d)',[Sender.Name,TThread.CurrentThread.ThreadID]));
end;