Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将多线程访问的ConcurrentBag转储到文件速度不够快_C#_Task Parallel Library - Fatal编程技术网

C# 将多线程访问的ConcurrentBag转储到文件速度不够快

C# 将多线程访问的ConcurrentBag转储到文件速度不够快,c#,task-parallel-library,C#,Task Parallel Library,我构建了这段代码来并行处理大量字符串之间的字符串比较,以加快速度 我使用了ConcurrentBag,因此所有线程(任务)都可以写入线程安全集合。然后我将这个集合转储到一个文件中 我遇到的问题是,我转储到文件中的ConcurrentBag日志的填充速度快于它写入文件的速度。因此,我的程序不断消耗越来越多的ram,直到内存耗尽 我的问题是我能做什么?改进对日志的写入?暂停任务,直到ConcurrentBag被转储,然后继续任务?最快的选择是什么 代码如下: CsvWriter csv = new

我构建了这段代码来并行处理大量字符串之间的字符串比较,以加快速度

我使用了ConcurrentBag,因此所有线程(任务)都可以写入线程安全集合。然后我将这个集合转储到一个文件中

我遇到的问题是,我转储到文件中的
ConcurrentBag日志
的填充速度快于它写入文件的速度。因此,我的程序不断消耗越来越多的ram,直到内存耗尽

我的问题是我能做什么?改进对日志的写入?暂停任务,直到ConcurrentBag被转储,然后继续任务?最快的选择是什么

代码如下:

CsvWriter csv = new CsvWriter(@"C:\test.csv");

List<Bailleur> bailleurs = DataLoader.LoadBailleurs();
ConcurrentBag<string> log = new ConcurrentBag<string>();
int i = 0;

var taskWriteToLog = new Task(() =>
{
    // Consume the items in the bag
    string item;
    while (true)  //  (!log.IsEmpty)
    {
        if (!log.IsEmpty)
        {
            if (log.TryTake(out item))
            {
                csv.WriteLine(item);
            }
            else
                Console.WriteLine("Concurrent Bag busy");
        }
        else
        {
            System.Threading.Thread.Sleep(1000);
        }
    }
});

taskWriteToLog.Start();

Parallel.ForEach(bailleurs, s1 =>
{
    foreach (Bailleur s2 in bailleurs)
    {
        var lcs2 = LongestCommonSubsequenceExtensions.LongestCommonSubsequence(s1.Name, s2.Name);
        string line = String.Format("\"LCS\",\"{0}\",\"{1}\",\"{2}\"", s1.Name, s2.Name, lcs2.Item2);
        log.Add(line);
        // Console.WriteLine(line);

        var dic = DiceCoefficientExtensions.DiceCoefficient(s1.Name, s2.Name);
        line = String.Format("\"DICE\",\"{0}\",\"{1}\",\"{2}\"", s1.Name, s2.Name, dic);
        log.Add(line);
        // Console.WriteLine(line);
    }
    i++;
    Console.WriteLine(i);
});

public class CsvWriter
{
    public string FilePath { get; set; }
    private FileStream _fs { get; set; }
    private StreamWriter _sw { get; set; }

    public CsvWriter2(string filePath)
    {
        FilePath = filePath;
        _fs = new FileStream(FilePath, FileMode.Create, FileAccess.Write);
        _sw = new StreamWriter(_fs);
    }

    public void WriteLine(string line)
    {
        _sw.WriteLine(line);
    }
}
CsvWriter csv=新的CsvWriter(@“C:\test.csv”);
List bailleurs=DataLoader.LoadBailleurs();
ConcurrentBag日志=新ConcurrentBag();
int i=0;
var taskWriteToLog=新任务(()=>
{
//把袋子里的东西吃掉
字符串项;
while(true)/(!log.IsEmpty)
{
如果(!log.IsEmpty)
{
if(记录尝试(输出项))
{
csv.WriteLine(项目);
}
其他的
Console.WriteLine(“并发包忙”);
}
其他的
{
系统线程线程睡眠(1000);
}
}
});
taskWriteToLog.Start();
Parallel.ForEach(bailleurs,s1=>
{
foreach(收银台s2收银台)
{
var lcs2=LongestCommonSubsequenceExtensions.LongestCommonSubsequence(s1.Name,s2.Name);
string line=string.Format(“\'LCS\”、“{0}\”、“{1}\”、“{2}\”、s1.Name、s2.Name、lcs2.Item2);
log.Add(行);
//控制台写入线(行);
var dic=DiceCoefficientExtensions.DiceCoefficient(s1.Name,s2.Name);
line=String.Format(“\'DICE\”,“{0}\”,“{1}\”,“{2}\”,s1.Name,s2.Name,dic);
log.Add(行);
//控制台写入线(行);
}
i++;
控制台写入线(i);
});
公共类CSV编写器
{
公共字符串文件路径{get;set;}
私有文件流_fs{get;set;}
私有StreamWriter _sw{get;set;}
公共CsvWriter2(字符串文件路径)
{
FilePath=FilePath;
_fs=新的文件流(FilePath,FileMode.Create,FileAccess.Write);
_sw=新的StreamWriter(_fs);
}
公共无效写线(字符串行)
{
_西南写入线(行);
}
}

使用
BlockingCollection
而不是
ConcurrentBag

BlockingCollection<string> log = new BlockingCollection<string>();
var item = log.Take();

首先,看起来您正在使用行作为块写入文件

如果您可以将所有数据放入对象中,并将其作为较大的块写入,则速度会更快。当前,您可能正在达到正在写入的设备的最大IOPS。你的线路会很小。因此,您的写入模式将看起来像4k随机IO。。或者更糟

使用不同的集合不会改变这样一个事实,即磁盘写入是您正在做的最慢的事情


看看concurrentbag,可能不太可能直接实现,但如果您可以从包中删除行并将它们合并到一个接近1-5MB的大字符串/字节数组中,您应该可以提高性能。(您可能需要将CR LF重新插入字符串中。)

不要直接使用并发包,请使用将并发包作为备份存储的包(默认情况下,它是一个并发队列)

其中一个允许您设置集合大小的上限,如果袋子装满,它将阻止插入线程,直到有空间插入为止

它还为您提供了方便,使您可以很容易地从包中取出物品,您只需在foreach循环中使用它,它将不断提供您的消费者数据,直到调用为止。在这之后,它会一直运行,直到行李变空,然后像任何其他已完成的正常
IEnumerable
一样退出。如果包在完成添加之前“变干”,它将阻塞线程,并在向包中放入更多数据时自动重新启动

void ProcessLog()
{
    CsvWriter csv = new CsvWriter(@"C:\test.csv");

    List<Bailleur> bailleurs = DataLoader.LoadBailleurs();

    const int MAX_BAG_SIZE = 500;
    BlockingCollection<string> log = new BlockingCollection<string>(new ConcurrentBag<string>(), MAX_BAG_SIZE);

    int i = 0;

    var taskWriteToLog = new Task(() =>
    {
        // Consume the items in the bag, no need for sleeps or poleing, When items are available it runs, when the bag is empty but CompletedAdding has not been called it blocks.
        foreach(string item in log.GetConsumingEnumerable())
        {
            csv.WriteLine(item);
        }
    });

    taskWriteToLog.Start();

    Parallel.ForEach(bailleurs, s1 =>
    {
        //Snip... You can switch to BlockingCollection without any changes to this section of code.
    });

    log.CompleteAdding(); //lets anyone using GetConsumingEnumerable know that no new items are comming so they can leave the foreach loops when the bag becomes empty.
}
void ProcessLog()
{
CsvWriter csv=新的CsvWriter(@“C:\test.csv”);
List bailleurs=DataLoader.LoadBailleurs();
const int最大行李尺寸=500;
BlockingCollection日志=新建BlockingCollection(新建ConcurrentBag(),最大行李大小);
int i=0;
var taskWriteToLog=新任务(()=>
{
//消费包中的物品,无需睡眠或极化,当物品可用时,它运行,当包为空但已完成时添加未被称为阻止。
foreach(log.getconsumineGenumerable()中的字符串项)
{
csv.WriteLine(项目);
}
});
taskWriteToLog.Start();
Parallel.ForEach(bailleurs,s1=>
{
//剪断…您可以切换到BlockingCollection,而无需对此部分代码进行任何更改。
});
log.CompleteAdding();//让任何使用GetConsuminagenumerable的人都知道没有新项目提交,这样他们就可以在包变空时离开foreach循环。
}

甚至不必为
获取
而烦恼,而(true)
使用
foreach
循环和
GetConsumingEnumerable()
这里的问题似乎是集合填充得太快,而不是太慢
BlockingCollection
也可以帮助您,但您需要显式设置它的容量。@s单击它可能因为
Thread.Sleep而填充得太快。BlockingCollection可能有帮助…@I4V好的,我没注意到。但我的观点是,不使用
Thread.Sleep()
可能会有所帮助(例如,当硬件发生变化时,这个问题可能会再次出现)。但设置
BlockingCollection
的容量肯定会有所帮助。这样,您就可以确保不会耗尽内存。除非您将设置为
WriteThrough
,否则它将缓冲写入,而且是顺序写入而不是随机写入,他不会在调用之间寻找,因此性能不会太差。是的,Scott是真的,IO一点也不差。我只能得到比我用t写的更多的数据
void ProcessLog()
{
    CsvWriter csv = new CsvWriter(@"C:\test.csv");

    List<Bailleur> bailleurs = DataLoader.LoadBailleurs();

    const int MAX_BAG_SIZE = 500;
    BlockingCollection<string> log = new BlockingCollection<string>(new ConcurrentBag<string>(), MAX_BAG_SIZE);

    int i = 0;

    var taskWriteToLog = new Task(() =>
    {
        // Consume the items in the bag, no need for sleeps or poleing, When items are available it runs, when the bag is empty but CompletedAdding has not been called it blocks.
        foreach(string item in log.GetConsumingEnumerable())
        {
            csv.WriteLine(item);
        }
    });

    taskWriteToLog.Start();

    Parallel.ForEach(bailleurs, s1 =>
    {
        //Snip... You can switch to BlockingCollection without any changes to this section of code.
    });

    log.CompleteAdding(); //lets anyone using GetConsumingEnumerable know that no new items are comming so they can leave the foreach loops when the bag becomes empty.
}