Почему потоки работают последовательно в этом консольном приложении?

Я создаю консольное приложение, которое должно выполнить несколько потоков для выполнения задачи. Моя проблема состоит в том, что потоки работают один за другим (thread1, запускаются-> работа-> конец и ТОЛЬКО затем запускают thread2) вместо того, чтобы выполнить все в то же время. Также я не хочу, чтобы больше чем 10 потоков работали в то же время (проблемы производительности). Рев является примером кода консольного приложения и используемого datamodule. мое приложение работает над тем же способом. я использовал datamodule, потому что после того, как потоки закончены, я должен заполнить базу данных той информацией. Также существуют комментарии в коде для, объясняют, который является причиной того, чтобы сделать что-то.

код консоли приложений:

    program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Unit1 in 'Unit1.pas' {DataModule1: TDataModule};

var dm:TDataModule1;
begin
   dm:=TDataModule1.Create(nil);
   try
     dm.execute;
   finally
    FreeAndNil(dm);
   end;
end.

и код datamodule

    unit Unit1;

interface

uses
  SysUtils, Classes, SyncObjs, Windows, Forms;

var   FCritical: TRTLCriticalSection;//accessing the global variables  

type
  TTestThread = class(TThread)
  protected
    procedure Execute;override;
  end;
  TDataModule1 = class(TDataModule)
    procedure DataModuleCreate(Sender: TObject);
    procedure DataModuleDestroy(Sender: TObject);
  private
    { Déclarations privées }
  public

    procedure execute;
    procedure CreateThread();
    procedure Onterminatethrd(Sender: TObject);
  end;

var
  DataModule1       : TDataModule1;
  FthreadCount      : Integer;  //know how many threads are running


implementation

{$R *.dfm}

{ TTestThread }

procedure TTestThread.Execute;
var
  f                 : TextFile;
  i                 : integer;
begin
  EnterCriticalSection(fcritical);
  AssignFile(f, 'd:\a' + inttostr(FthreadCount) + '.txt');
  LeaveCriticalSection(fcritical);
  Rewrite(f);
  try
    i := 0;
    while i <= 1000000 do // do some work...
      Inc(i);
    Writeln(f, 'done');
  finally
    CloseFile(f);
  end;
end;

{ TDataModule1 }

procedure TDataModule1.CreateThread;
var
  aThrd             : TTestThread;
begin
  aThrd := TTestThread.Create(True);
  aThrd.FreeOnTerminate := True;
  EnterCriticalSection(fcritical);
  Inc(FthreadCount);
  LeaveCriticalSection(fcritical);
  aThrd.OnTerminate:=Onterminatethrd;
  try
    aThrd.Resume;
  except
    FreeAndNil(aThrd);
  end;
end;

procedure TDataModule1.Onterminatethrd(Sender: TObject);
begin
  EnterCriticalSection(fcritical);
  Dec(FthreadCount);
  LeaveCriticalSection(fcritical);
end;

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  InitializeCriticalSection(fcritical);
end;

procedure TDataModule1.DataModuleDestroy(Sender: TObject);
begin
  DeleteCriticalSection(fcritical);
end;

procedure TDataModule1.execute;
var
  i                 : integer;
begin
  i := 0;
  while i < 1000 do
  begin
    while (FthreadCount = 10) do
      Application.ProcessMessages;//wait for an thread to finish. max threads at a //time =10

    CreateThread;

    EnterCriticalSection(fcritical);
    Inc(i);
    LeaveCriticalSection(fcritical);

    while FthreadCount > 0 do //wait for all threads to finish in order to close the //main thread
    begin
      Application.ProcessMessages;
      CheckSynchronize;
    end;
  end;
end;

end.

таким образом, поскольку я сказал, что проблема состоит в том, что мои потоки работают один за другим, вместо того, чтобы работать все в то же время. также я видел, что иногда только первый поток работал, после этого все, что остальные просто создают и законченный. в моем приложении весь код защищен попыткой-excepts, но никакие ошибки не повышены.

Кто-то может дать мне совет?

6
задан RBA 21 July 2016 в 10:20
поделиться

3 ответа

По крайней мере, вы должны поместить

while FthreadCount > 0 do //wait for all threads to finish in order to close the //main thread
begin
  Application.ProcessMessages;
  CheckSynchronize;
end;

за пределы главного цикла. Именно этот цикл ожидания и вызывает задержку. Для каждого целого числа i главного цикла он ждет, пока FThreadCount не упадет до нуля.

В качестве примечания: обычно вам не нужно защищать локальные переменные критическими секциями. Хотя наличие там сообщений процесса может испортить ситуацию, так как может вызвать реентерабельность.

7
ответ дан 17 December 2019 в 00:03
поделиться

Я последовал совету Марджана, и следующий код, похоже, работает правильно. Я отвечаю на свой вопрос, чтобы предоставить код ответа, который могут анализировать другие и при необходимости исправлять.

unit Unit1;

interface

uses
  SysUtils, Classes, SyncObjs, Windows, Forms, Dialogs;

var   FCritical: TRTLCriticalSection;  

type
  TTestThread = class(TThread)
  protected
    procedure Execute;override;
  end;
  TDataModule1 = class(TDataModule)
    procedure DataModuleCreate(Sender: TObject);
    procedure DataModuleDestroy(Sender: TObject);
  private
    { Déclarations privées }
  public

    procedure execute;
    procedure CreateThread();
    procedure Onterminatethrd(Sender: TObject);
  end;

var
  DataModule1       : TDataModule1;
  FthreadCount      : Integer;


implementation

{$R *.dfm}

{ TTestThread }

procedure TTestThread.Execute;
var
  f                 : TextFile;
  i                 : integer;

begin
 AssignFile(f, 'd:\a\a' + inttostr(FthreadCount) + '.txt');
 if fileexists('d:\a\a' + inttostr(FthreadCount) + '.txt') then
  Append(f)
 else
  Rewrite(f);
   try
    i := 0;
    while i <= 1000000 do
      Inc(i);
  Writeln(f, 'done '+floattostr(self.Handle));
  finally
    CloseFile(f);
  end;
end;

{ TDataModule1 }

procedure TDataModule1.CreateThread;
var
  aThrd             : TTestThread;
begin
  aThrd := TTestThread.Create(True);
  aThrd.FreeOnTerminate := True;
  EnterCriticalSection(fcritical);
  Inc(FthreadCount);
  LeaveCriticalSection(fcritical);
  aThrd.OnTerminate:=Onterminatethrd;
  try
    aThrd.Resume;
  except
    FreeAndNil(aThrd);
  end;
end;

procedure TDataModule1.Onterminatethrd(Sender: TObject);
begin
  EnterCriticalSection(fcritical);
    Dec(FthreadCount);
  LeaveCriticalSection(fcritical);
end;

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  InitializeCriticalSection(fcritical);
end;

procedure TDataModule1.DataModuleDestroy(Sender: TObject);
begin
  DeleteCriticalSection(fcritical);
end;

procedure TDataModule1.execute;
var
  i                 : integer;
begin
  i := 0;
 try
  while i < 1000 do
  begin
    while (FthreadCount = 10) do
     begin
      Application.ProcessMessages;
      CheckSynchronize
     end;
    CreateThread;
    Inc(i);
  end;
    while FthreadCount > 0 do
    begin
      Application.ProcessMessages;
      CheckSynchronize;
    end;
 except on e:Exception do
//
 end;
end;

end.

на данный момент я тестировал этот код несколько раз, и, похоже, он работает нормально. Если Роб ответит мне небольшим примером того, как я могу реализовать семафоры для решения этой проблемы, я также опубликую здесь весь код.

1
ответ дан 17 December 2019 в 00:03
поделиться

У меня есть устройство, которое делает именно то, что вам нужно. Просто скачайте его по адресу:

Cromis.Threading

Внутри у вас есть два класса:

  1. TTaskPool: Пул задач. Простой способ делать вещи асинхронными.
  2. TTaskQueue: очередь асинхронных задач. Работает как стандартная очередь FIFO.

TTaskQueue может использоваться автономно, например, с простыми ванильными потоками. Он блокируется внутри одного потока и ставит запросы в очередь.

Если этого недостаточно, вы можете проверить OmniThreadLibrary по адресу:

OmniThreadLibrary

Это мощная библиотека потоков, намного превосходящая то, что есть у меня. Но также более сложный в использовании (но все же очень простой по сравнению с классической потоковой передачей).

-1
ответ дан 17 December 2019 в 00:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: