C# 如何在控制台应用程序.net中有效地使用线程
我有8个核心系统,我正在处理大量文本文件,其中包含数百万行,比如23个文件包含大量行,需要2到3个小时才能完成。我正在考虑使用TPL任务来处理文本文件。到目前为止,我正在使用的代码是按顺序逐个处理文本文件,所以我正在考虑像这样拆分它一个线程中有5个文本文件,另一个线程中有5个文本文件,等等。这是一个好方法还是其他方法?我使用的是.NET4.0,我使用的代码如下所示C# 如何在控制台应用程序.net中有效地使用线程,c#,multithreading,.net-4.0,task-parallel-library,C#,Multithreading,.net 4.0,Task Parallel Library,我有8个核心系统,我正在处理大量文本文件,其中包含数百万行,比如23个文件包含大量行,需要2到3个小时才能完成。我正在考虑使用TPL任务来处理文本文件。到目前为止,我正在使用的代码是按顺序逐个处理文本文件,所以我正在考虑像这样拆分它一个线程中有5个文本文件,另一个线程中有5个文本文件,等等。这是一个好方法还是其他方法?我使用的是.NET4.0,我使用的代码如下所示 foreach (DataRow dtr in ds.Tables["test"].Rows) {
foreach (DataRow dtr in ds.Tables["test"].Rows)
{
string filename = dtr["ID"].ToString() + "_cfg";
try
{
foreach (var file in
Directory.EnumerateFiles(Path.GetDirectoryName(dtr["FILE_PATH"].ToString()), "*.txt"))
{
id = file.Split('\\').Last();
if (!id.Contains("GMML"))
{
strbsc = id.Split('_');
id = strbsc[0];
}
else
{
strbsc = file.Split('-');
id = ("RC" + strbsc[1]).Replace("SC", "");
}
ProcessFile(file, id, dtr["CODE"].ToString(), dtr["DOR_CODE"].ToString(), dtr["FILE_ID"].ToString());
}
}
如何将文本文件分为多个批,每个批应该在线程中运行,而不是一个一个地运行。假设如果23个文件,那么一个线程中7个文件,一个线程中7个文件,另一个线程中2个文件。还有一件事是,我正在将所有这些数据从文本文件移动到oracle数据库
编辑
如果我像这样使用它,那么它是值得的,但是如何将文件分批处理呢
Task.Factory.StartNew(() => {ProcessFile(file, id, dtr["CODE"].ToString(), dtr["DOR_CODE"].ToString(), dtr["FILE_ID"].ToString()); });
将文件分成多个块似乎不是一个好主意,因为它的性能提升与文件在磁盘上的放置方式有关。但由于磁盘IO操作的异步性质,我强烈建议异步访问该文件。有几种方法可以做到这一点,您总是可以选择这些方法的组合。 在最低级别,您可以使用异步方法,如StreamWriter.WriteAsync()或StreamReader.ReadAsync()来访问磁盘上的文件,并协同让操作系统知道它可以切换到磁盘IO的新线程,并释放线程,直到磁盘IO操作完成。虽然在这个级别进行异步调用很有用,但它本身并不会对应用程序的整体性能产生重大影响,因为应用程序仍在等待磁盘操作完成,而在此期间什么也不做!(当从UI线程调用这些调用时,它们会对软件的响应性产生很大影响) 因此,我建议将软件逻辑拆分为至少两个独立的部分,在两个独立的线程上运行;一个用于从文件中读取数据,另一个用于处理读取的数据。您可以使用提供者/使用者模式来帮助这些线程进行交互。 net提供的一个很好的数据结构是System.Collections.Concurrent.ConcurrentQueue,它在实现多线程提供者/使用者模式时特别有用 因此,您可以轻松地执行以下操作:
System.Collections.Concurrent.ConcurrentQueue<string> queue = new System.Collections.Concurrent.ConcurrentQueue<string>();
bool readFinished = false;
Task tRead = Task.Run(async () =>
{
using (FileStream fs = new FileStream())
{
using (StreamReader re = new StreamReader(fs))
{
string line = "";
while (!re.EndOfStream)
queue.Enqueue(await re.ReadLineAsync());
}
}
});
Task tLogic = Task.Run(async () =>
{
string data ="";
while (!readFinished)
{
if (queue.TryDequeue(out data))
//Process data
else
await Task.Delay(100);
}
});
tRead.Wait();
readFinished = true;
tLogic.Wait();
System.Collections.Concurrent.ConcurrentQueue=new System.Collections.ConcurrentQueue();
bool readFinished=false;
Task-tRead=Task.Run(异步()=>
{
使用(FileStream fs=new FileStream())
{
使用(StreamReader re=新StreamReader(fs))
{
字符串行=”;
而(!re.EndOfStream)
排队(等待re.ReadLineAsync());
}
}
});
Task tLogic=Task.Run(异步()=>
{
字符串数据=”;
而(!readFinished)
{
if(queue.TryDequeue(out数据))
//过程数据
其他的
等待任务。延迟(100);
}
});
等一下;
readFinished=true;
tLogic.Wait();
这个简单的示例使用StreamReader.ReadLineAsync()从文件中读取数据,而一个好的做法是将固定长度的字符读入char[]缓冲区,然后将该数据添加到队列中。您可以在一些测试之后找到优化的缓冲区长度。将文件拆分为多个块似乎不是一个好主意,因为它的性能提升与文件在磁盘上的放置方式有关。但由于磁盘IO操作的异步性质,我强烈建议异步访问该文件。有几种方法可以做到这一点,您总是可以选择这些方法的组合。 在最低级别,您可以使用异步方法,如StreamWriter.WriteAsync()或StreamReader.ReadAsync()来访问磁盘上的文件,并协同让操作系统知道它可以切换到磁盘IO的新线程,并释放线程,直到磁盘IO操作完成。虽然在这个级别进行异步调用很有用,但它本身并不会对应用程序的整体性能产生重大影响,因为应用程序仍在等待磁盘操作完成,而在此期间什么也不做!(当从UI线程调用这些调用时,它们会对软件的响应性产生很大影响) 因此,我建议将软件逻辑拆分为至少两个独立的部分,在两个独立的线程上运行;一个用于从文件中读取数据,另一个用于处理读取的数据。您可以使用提供者/使用者模式来帮助这些线程进行交互。 net提供的一个很好的数据结构是System.Collections.Concurrent.ConcurrentQueue,它在实现多线程提供者/使用者模式时特别有用 因此,您可以轻松地执行以下操作:
System.Collections.Concurrent.ConcurrentQueue<string> queue = new System.Collections.Concurrent.ConcurrentQueue<string>();
bool readFinished = false;
Task tRead = Task.Run(async () =>
{
using (FileStream fs = new FileStream())
{
using (StreamReader re = new StreamReader(fs))
{
string line = "";
while (!re.EndOfStream)
queue.Enqueue(await re.ReadLineAsync());
}
}
});
Task tLogic = Task.Run(async () =>
{
string data ="";
while (!readFinished)
{
if (queue.TryDequeue(out data))
//Process data
else
await Task.Delay(100);
}
});
tRead.Wait();
readFinished = true;
tLogic.Wait();
System.Collections.Concurrent.ConcurrentQueue=new System.Collections.ConcurrentQueue();
bool readFinished=false;
Task-tRead=Task.Run(异步()=>
{
使用(FileStream fs=new FileStream())
{
使用(StreamReader re=新StreamReader(fs))
{
字符串行=”;
而(!re.EndOfStream)
排队(等待re.ReadLineAsync());
}
}
});
Task tLogic=Task.Run(异步()=>
{
字符串数据=”;
而(!readFinished)
{
if(queue.TryDequeue(out数据))
//过程数据
其他的
等待任务。延迟(100);
}
});
等一下;
readFinished=true;
tLogic.Wait();
这个简单的示例使用StreamReader.ReadLineAsync()从文件中读取数据,而一个好的做法是将固定长度的字符读入char[]缓冲区,然后将该数据添加到队列中。您可以在一些测试之后找到优化的缓冲区长度。总之,真正的瓶颈是当我进行大规模插入时,我正在检查插入数据是否存在于数据库中,或者什么,我有一个状态列,其中