C# 如何平衡.NET中的读写线程

C# 如何平衡.NET中的读写线程,c#,.net,multithreading,C#,.net,Multithreading,问题:读取的数据堆积起来,等待写入 我有一个基本的ETL过程,读取文件,转换数据,然后将数据写入另一个文件。因为我在一个多核系统上,所以我尝试使用多个线程来执行这个操作。我的问题是,读者的速度超过了作者:许多文件最终被读取,数据被转换,但它们堆积起来等待写入 我想要的是在读取文件和写入文件之间保持平衡,同时仍然使用多线程 我在.NET库(C#4.0)中尝试了各种方法。我认为有一些东西我不明白,这肯定比简单地使用Thread或ThreadPool.QueueUserWorkItem或Task要复杂

问题:读取的数据堆积起来,等待写入

我有一个基本的ETL过程,读取文件,转换数据,然后将数据写入另一个文件。因为我在一个多核系统上,所以我尝试使用多个线程来执行这个操作。我的问题是,读者的速度超过了作者:许多文件最终被读取,数据被转换,但它们堆积起来等待写入

我想要的是在读取文件和写入文件之间保持平衡,同时仍然使用多线程

我在.NET库(C#4.0)中尝试了各种方法。我认为有一些东西我不明白,这肯定比简单地使用
Thread
ThreadPool.QueueUserWorkItem
Task
要复杂得多,就像我在基本示例中看到的那样

例如,假设我尝试以下方法:

Task task = new Task(() => PerformEtl(sourceFile));
task.start();
如果我记录正在读取的文件和正在写入的文件,这是一个10:1的比例。在长期运行的过程中,这是不可持续的

一定有一些基本的多线程/多处理模式,我不知道或想不起来。有人知道我该去哪里吗?谢谢


已解决:

多亏了@Blam

下面是一些示例/伪代码,以说明如何使用.NET库实现生产者-消费者模式,如@Blam所建议的

// Adapted from: https://msdn.microsoft.com/en-us/library/dd997371(v=vs.100).aspx
BlockingCollection<object> dataItems = new BlockingCollection<object>(10);
List<Task> tasks = new List<Task>();

tasks.Add(
    // Producer.
    Task.Factory.StartNew(() =>
    {
        for (;;)
        {
            string filePath = GetNextFile();
            if (filePath == null) break;

            object data = ProcessData(ReadData(file));
            dataItems.Add(data);
        }

        dataItems.CompleteAdding();
    })
);

tasks.Add(
    // Consumer.
    Task.Factory.StartNew(() =>
    {
        while (!dataItems.IsCompleted))
        {
            object data;

            try
            {
                data = dataItems.Take();
                WriteData(data);
            }
            catch(InvalidOperationException ioe)
            {
                Console.Error.WriteLine(ioe.Message);
            }
        }
    })
);

Task.WaitAll(tasks.ToArray());
//改编自:https://msdn.microsoft.com/en-us/library/dd997371(v=vs.100).aspx
BlockingCollection数据项=新BlockingCollection(10);
列表任务=新列表();
任务。添加(
//制片人。
Task.Factory.StartNew(()=>
{
对于(;;)
{
字符串filePath=GetNextFile();
如果(filePath==null)中断;
对象数据=ProcessData(ReadData(file));
添加(数据);
}
dataItems.CompleteAdding();
})
);
任务。添加(
//消费者。
Task.Factory.StartNew(()=>
{
而(!dataItems.IsCompleted))
{
对象数据;
尝试
{
data=dataItems.Take();
写入数据(数据);
}
捕获(无效操作异常ioe)
{
Console.Error.WriteLine(ioe.Message);
}
}
})
);
Task.WaitAll(tasks.ToArray());

MSDN的讨论在这里:

我就是这样做的,我把它分成3部分

  • 阅读
    只有一组头部-并行执行此操作没有任何好处
    关闭文件并将文本传递到下一步
  • 过程
与上限(有界容量)一起使用
上界时,快步不会比慢步走得太远

所以你有多个核心。你可能是被我束缚住了

您可以并行处理(步骤2),但除非您有一些复杂的转换,否则不会产生任何影响


尝试在不同的物理设备上读写。

必须限制写入程序。这可以像信号量一样简单,在writer中调用WaitOne(),在readers中调用Release()。为什么不运行N个线程,每个线程都执行
,而(1){Read();Transform();Write();}
。无需节流。“节流写入程序”?我不应该限制读者的阅读吗?我想让读者放慢速度。@usr是的,我本质上想分配一定数量的线程,并将线程上运行的任务排队。但是,这在.NET中是如何工作的呢?(我是.NET的新手。)你能给我指出正确的方向吗?我认为框架应该决定线程的最佳数量。我认为消费者和制作人的意见是相反的。这听起来很有希望。我本应该想到“生产者-消费者”这个词,但我没有想到。我会研究你的建议。这很有效!谢谢我已经编辑了我的答案,为解决方案添加了一些伪代码。