如何在旧线程结束之前暂停创建新线程

时间:2018-05-02 18:26:09

标签: c# multithreading file asynchronous

背景

我有一个非常大的二进制数据文件(20+ GB),我需要解析,处理数据,然后写我的输出。我在处理如此大量的数据方面经验很少,虽然我在构思如何处理它时遇到了一些麻烦,但我确实有一个想法。注意:输入数据包含从IBM大型机检索到的大量记录,因此格式如下:

每条记录(行/行)的前4个字节是RDW(记录描述符字)。 RDW包含记录的长度(包括RDW)。由于RDW,即使文件是一个恒定的字节流,我也知道每个记录的结束位置。我可以将这个二进制文件转换为一个文本文件,将每两个字节转换为十六进制表示,并在记录末尾包含一个新的行字符,但我担心如果一个20多GB的二进制文件有多大,如果这样翻译。

因为我想将文件保留为二进制文件,所以我知道如何继续:

  1. 使用一个“主”线程顺序读取文件。
  2. 一旦主服务器到达记录的末尾(使用RDW中的信息),它就会生成一个新的“worker”线程,并将从该文件读取的数据传递给该线程。
    • 工作线程解析数据,处理数据并将其输出到某处。 (我想我会将数据保存在SQLite数据库中。)
  3. 当工作线程正在工作时,主线程继续读取该文件,当它完成读取另一条记录时,它会产生第二个工作线程来处理第二条记录。这将持续到所有记录都已处理完毕。
  4. 问题

    不幸的是,我设想了一个问题。读取“主”线程的工作速度比它产生的线程快得多,我担心会创建太多的线程。为了防止这种情况,我设想了这个解决方案(伪代码):

    record = file.ReadRecord()
    if(numberOfRunningWorkerThreads < MAX_THREADS)
        SpawnWorkerThread(record);
    else
        WaitUntil(numberOfRunningWorkerThreads < MAX_THREADS)
    

    但是,我不知道如何实现这样的功能,尤其是最后else条件。我是多线程和异步的新手,我甚至不确定这两个术语之间的区别是什么。

    有人能指出我正确的方向吗?

2 个答案:

答案 0 :(得分:2)

我相信你正在寻找一个信号量(或者也许SemaphoreSlim可能也适合你)。信号量“限制可以同时访问资源或资源池的线程数”。信号量创建时具有特定数量的插槽。然后,您可以调用“WaitOne”等待可用插槽,并在完成插槽时调用“Release”。如果没有可用的插槽,“WaitOne”可以永远等待,或者直到发生超时。

因此,在您的情况下,主线程将调用WaitOne以等待可用的插槽。然后,在工作线程结束时,您可以调用Release来释放一个插槽。

.NET信号量: https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx

.NET SemaphoreSlim(轻量级信号量): https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx

答案 1 :(得分:2)

<强>解决方法1:

使用ThreadPool。设置MaxThreads,其中

  

设置可以同时处于活动状态的线程池的请求数。高于该数字的所有请求将保持排队,直到线程池线程可用。

类似的东西:

System.Threading.ThreadPool.SetMaxThreads(50, 1000);
// inside loop
ThreadPool.QueueUserWorkItem(ProcessRequest);
// end loop

ProcessRequest是您开展工作的方法。

<强>溶液2:

如果您知道记录数量:请使用Parallel.For并相应地设置MaxDegreeOfParallelism

Parallel.For(0, 1000, new ParallelOptions { MaxDegreeOfParallelism = 10 },
i => { 
    ProcessRequest(i);
});