如何正确地限制多线程应用程序?

Jos*_*ith 2 c# multithreading sleep throttling

我有一个运行在64位Windows 2008 r2服务器上的ac #console应用程序,该服务器也承载MSSQL Server 2005.

此应用程序通过文本文件运行,读取行,将行值拆分为变量,并将数据插入到localhost托管的SQL数据库中.

每个Text文件都是一个新线程,每一行都是一个新线程,每个SQL insert语句都在一个新线程下执行.

我计算这些类型的线程中的每一个的数量,并在它们完成时递减.我想知道最好的方法是"打开"未来的线程......

例如..在打开一个新的SQL插入线程之前,我正在调用...

while(numberofcurrentthreads > specifiednumberofthreads)
{
// wait
}
new.Thread(insertSQL);
Run Code Online (Sandbox Code Playgroud)

其中,指定的数字线程数被估计为不会抛出System.OutofMemoryExceptions的值.很多猜测工作已经用于确定每个过程的数量.

我的问题是......是否有更"有效"或正确的方法来做到这一点?有没有办法读取系统内存,而不是物理内存,并根据指定的资源分配等待?

为了说明这个想法......

while(System.Memory < (System.Memory/2) || System.OutofMemory == true)
{
// wait
}
new.Thread(insertSQL);
Run Code Online (Sandbox Code Playgroud)

我正在使用的当前方法工作​​并在一个不错的时间完成..但它可以做得更好.通过该过程的一些文本文件比其他文本文件更大,并不一定能最好地利用系统资源......

例如,如果我说两个文本文件在两个文本文件<300KB时都能正常工作.如果一个或两个超过100,000KB,它就不能很好地工作.

似乎还有一个"黄油区",事物处理效率最高.平均约占所有CPU资源的75%.将这些值调高得过高,它将以100%的CPU运行,但由于无法跟上,因此处理速度较慢.

Jim*_*hel 5

为每个文件以及每个行和每个SQL插入语句创建一个新线程是很疯狂的.使用三个线程和一个链式生产者 - 消费者模型可能会好得多,所有这些都通过线程安全的队列进行通信.在C#中,那将是BlockingCollection.

首先,设置两个队列,一个用于从文本文件中读取的行,另一个用于已处理的行:

const int MaxQueueSize = 10000;
BlockingCollection<string> _lines = new BlockingCollection<string>(MaxQueueSize);
BlockingCollection<DataObject> _dataObjects = new BlockingCollection<DataObject>(MaxQueueSize);
Run Code Online (Sandbox Code Playgroud)

DataObject顺便说一下,我正在调用你要插入数据库的对象.你没有说那是什么.对于本讨论的目的而言,这并不重要,但您可以用您用来表示已处理字符串的任何类型替换它.

现在,您创建三个线程:

  1. 一个线程,逐行读取文本文件并将行放入_lines队列.
  2. 一个行处理器,_lines它从队列中逐个读取行,处理它,然后创建一个DataObject然后放在_dataObjects队列中的行.
  3. 一个读取_dataObjects队列并将其插入数据库的线程.

除了简单性(这很容易组合在一起),这个模型有很多好处.

首先,同时从磁盘读取多个线程通常会导致性能降低,因为磁盘驱动器一次只能执行一项操作.让多个线程同时击中磁盘会导致不必要的磁头搜索.只需一个线程就可以保持输入队列满.

其次,限制队列的大小可以防止内存不足.当磁盘读取线程尝试将第10,001项插入队列时,它将等待处理线程删除项目.那是"阻塞"的一部分BlockingCollection.

您可能会发现,您可以通过对SQL插入进行分组并一次发送一堆记录来加速您的SQL插入,这实际上是一次大量插入100或1000条记录,而不是发送100或1000个单独的事务.

此解决方案可防止线程过多的问题.您有一定数量的线程,所有线程都尽可能快地运行.并且通过限制队列中可以存在的事物的数量来限制内存使用.

该解决方案也可以很好地扩展.如果您有多个驱动器上的文件,则可以添加第二个文件读取线程以从该另一个物理驱动器读取文件并将这些行放在同一队列中.BlockingCollection支持多个生产者和多个消费者,因此添加另一个生产者根本不是问题.

消费者也是如此.如果您发现处理步骤是瓶颈,则可以添加另一个处理线程.它也将从_lines队列中读取并写入dataObjects队列.

但是,拥有比处理器内核更多的线程可能会使程序变慢.如果你有一个四核处理器,那么创建8个处理线程对你没有任何帮助.它会使事情变得更慢,因为操作系统将花费大量时间在线程上下文切换而不是做有用的工作.

你必须做一些调整才能获得最佳性能.队列大小应该足够大,以支持连续的工作流程(因此没有线程缺乏工作,或者花费太多时间等待输出队列),但是没有太大的时间来满足内存.根据三个阶段的相对速度,其中一个队列可能必须比另一个队列大.如果三个阶段中的一个是瓶颈,您可以在该阶段添加另一个线程来帮助.

我使用文本文件创建了一个简单的模型示例,用于输入和输出.根据您的情况扩展应该很容易.请参阅简单多线程,以及后续内容,第2部分.