线程的性能改进

时间:2012-02-12 02:19:02

标签: multithreading delphi

我从未使用过线程 - 从未想过我的代码会受益。但是,我认为线程可能会提高以下伪代码的性能:

Loop through table of records containing security symbol field and a quote field
    Load a web page (containing a security quote for a symbol) into a string variable
    Parse the string for the quote
    Save the quote in the table
    Get next record
end loop

加载每个网页需要花费大量时间。解析报价非常快。我想我可以拿一个线程的一半记录,然后在另一个线程中处理另一半。

2 个答案:

答案 0 :(得分:4)

如果记录数量相对较小,比如50或更少,你可以为每条记录启动一个单独的线程,让它们全部并行运行,例如:

begin thread
  Load a web page for symbol into a string variable
  Parse the string for the quote
  Save the quote in the table
end thread

Loop through table of records
  Launch a thread for current security symbol
  Get next record
end loop

如果您要处理更多的记录,请考虑使用线程池,以便您可以处理较小批量的记录,例如:

Create X threads
Put threads in a list

Loop through table of records
  Wait until a thread in pool is idle
  Get idle thread from pool
  Assign current security symbol to thread
  Signal thread
  Get next record
end loop

Wait for all threads to be idle
Terminate threads

begin thread
  Loop until terminated
    Mark idle
    Wait for signal
    If not Terminated
      Load a web page for current symbol into a string variable
      Parse the string for the quote
      Save the quote in the table
    end if
  end loop
end thread

有许多不同的方法可以实现上述功能,这就是我将其保留为伪代码的原因。查看VCL的TThreadTListTEvent类,或Win32 API QueueUserWorkerItem()函数或任意数量的第三方线程库。

答案 1 :(得分:4)

OmniThreadLibrary中,使用多级管道解决此问题非常简单 - 第一阶段在多个任务上运行,在一个实例中下载网页和第二阶段运行,并将数据存储到数据库中。我不久前写了一篇blog post来记录这个解决方案。

解决方案可以用以下代码总结(你必须在HttpGet和Inserter方法中填写一些地方)。

uses
  OtlCommon,
  OtlCollections,
  OtlParallel;

function HttpGet(url: string; var page: string): boolean;
begin
  // retrieve page contents from the url; return False if page is not accessible
end;

procedure Retriever(const input: TOmniValue; var output: TOmniValue);
var
  pageContents: string;
begin
  if HttpGet(input.AsString, pageContents) then
    output := TPage.Create(input.AsString, pageContents);
end;

procedure Inserter(const input, output: IOmniBlockingCollection);
var
  page   : TOmniValue;
  pageObj: TPage;
begin
  // connect to database
  for page in input do begin
    pageObj := TPage(page.AsObject);
    // insert pageObj into database
    FreeAndNil(pageObj);
  end;
  // close database connection
end;

procedure ParallelWebRetriever;
var
  pipeline: IOmniPipeline;
  s       : string;
  urlList : TStringList;
begin
  // set up pipeline
  pipeline := Parallel.Pipeline
    .Stage(Retriever).NumTasks(Environment.Process.Affinity.Count * 2)
    .Stage(Inserter)
    .Run;
  // insert URLs to be retrieved
  for s in urlList do
    pipeline.Input.Add(s);
  pipeline.Input.CompleteAdding;
  // wait for pipeline to complete
  pipeline.WaitFor(INFINITE);
end;