多线程提高性能

6

我从来没有使用过线程——也从未想过我的代码会受益。然而,我认为线程可能会提高以下伪代码的性能:

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

加载每个网页需要最多的时间。解析报价非常快。我想我可以将一半的记录放在一个线程中处理,然后在第二个线程中处理另一半。


1
使用一个线程框架,你可以在许多StackOverflow的问题中找到相关参考(进行搜索)。 - Misha
5
请查看:http://www.thedelphigeek.com/2011/10/omnithreadlibrary-in-practice-1web.html - gabr
1
我会选择Omnithreadlibrary,它非常高效。顺便提一下,也看看Omnixml,这些库非常有帮助! - user497849
这是一个明显的情况,使用管道会很有意义。从听起来的声音来看,您需要更多的线程来下载,而不是解析它们。 - David Heffernan
感谢您的所有评论。在跟进您的评论后,我将会享受一些实验。 - Max Williams
如果你的评论是一个回答,我会接受它。谢谢。 - Max Williams
2个回答

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
我认为基于管道的解决方案在这里更加合适,因为任务的组成部分在性质上非常不同。下载文件是IO限制的,而解析文件则是CPU限制的。 - David Heffernan
@David:我需要对“流水线处理”进行一些研究,因为我从未听说过。如果有好的网址链接,将不胜感激。 - Max Williams

4
OmniThreadLibrary中,使用多阶段管道解决这个问题非常简单-第一阶段在多个任务上运行并下载网页,第二阶段在一个实例中运行并将数据存储到数据库中。我曾经写过一篇博客文章记录了这个解决方案。
以下代码概括了解决方案(您需要填写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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接