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

时间:2014-12-10 03:00:48

标签: c# multithreading sleep throttling

我有一个在64位Windows 2008 r2服务器上运行的c#控制台应用程序,该服务器也托管MSSQL Server 2005.

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

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

我计算这些类型的线程的数量,并在完成时递减。我想知道最好的方法是什么" pend"开放的未来线索...

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

while(numberofcurrentthreads > specifiednumberofthreads)
{
// wait
}
new.Thread(insertSQL);

如果指定的numbernumberofthreads估计为不会抛出System.OutofMemoryExceptions的值。很多猜测工作已经用于确定每个过程的数量。

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

说明这个想法......

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

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

例如,如果我说两个文本文件都是&lt; 300KB。如果一个或两个超过100,000KB,它就不能很好地工作。

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

1 个答案:

答案 0 :(得分:5)

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

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

const int MaxQueueSize = 10000;
BlockingCollection<string> _lines = new BlockingCollection<string>(MaxQueueSize);
BlockingCollection<DataObject> _dataObjects = new BlockingCollection<DataObject>(MaxQueueSize);
顺便说一下,

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

现在,您创建了三个线程:

  1. 逐行读取文本文件并将行放入_lines队列的线程。
  2. 一个行处理器,它从_lines队列逐个读取行,对其进行处理,然后创建一个DataObject,然后将其置于_dataObjects队列中。
  3. 读取_dataObjects队列并将其插入数据库的线程。
  4. 除了简单性(并且非常容易组合在一起),这个模型有很多好处。

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

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

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

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

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

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

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

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

    我使用文本文件创建了一个简单的模型示例,用于输入和输出。根据您的情况扩展应该很容易。请参阅Simple Multithreading和后续行动Part 2