C# 如何使用类任务进行并行处理
我是一名初级程序员,我正在努力解决一项任务。我正在使用c#.net 4.0浏览文件夹,选择所有*.xml文件,并将每个文件写入具有新扩展名*.bin的新文件夹。对于编写之前的每个文件,我都在应用另一个程序员编写的算法,我不知道它的实现 所以我读取了*.xml文件,对其进行反序列化并将其写入新的*.bin文件。当我没有使用并行编程时,我有1分钟的时间处理2000个文件。现在我决定将并行编程应用于任务。现在我为每个文件创建新任务(所有处理(读-反序列化-写)都在一个任务中),现在我有40秒。但我认为并行编程帮助我将时间缩短到25-30秒 请给出你的意见,我做错了什么,以及我如何认识到这一点。谢谢C# 如何使用类任务进行并行处理,c#,multithreading,c#-4.0,task-parallel-library,task,C#,Multithreading,C# 4.0,Task Parallel Library,Task,我是一名初级程序员,我正在努力解决一项任务。我正在使用c#.net 4.0浏览文件夹,选择所有*.xml文件,并将每个文件写入具有新扩展名*.bin的新文件夹。对于编写之前的每个文件,我都在应用另一个程序员编写的算法,我不知道它的实现 所以我读取了*.xml文件,对其进行反序列化并将其写入新的*.bin文件。当我没有使用并行编程时,我有1分钟的时间处理2000个文件。现在我决定将并行编程应用于任务。现在我为每个文件创建新任务(所有处理(读-反序列化-写)都在一个任务中),现在我有40秒。但我认为
byte[] buffer;
using (Stream stream = new FileInfo(file).OpenRead())
{
buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
}
foreach (var culture in supportedCultures)
{
CultureInfo currentCulture = culture;
Tasks.Add(Task.Factory.StartNew(() =>
{
var memoryStream = new MemoryStream(buffer);
Task<object> serializeTask = Task.Factory.StartNew(() =>
{
return typesManager.Load(memoryStream, currentCulture);
}, TaskCreationOptions.AttachedToParent);
string currentOutputDirectory = null;
if (outputDirectory != null)
{
currentOutputDirectory = outputDirectory.Replace(PlaceForCultureInFolderPath,
currentCulture
.ToString());
Directory.CreateDirectory(currentOutputDirectory);
}
string binFile = Path.ChangeExtension(Path.GetFileName(file), ".bin");
string binPath = Path.Combine(
currentOutputDirectory ?? Path.GetDirectoryName(file),
binFile);
using (FileStream outputStream = File.OpenWrite(binPath))
{
try
{
new BinaryFormatter().Serialize(outputStream,serializeTask.Result);
}
catch (SerializationException e)
{
ReportCompilationError(e.Message, null);
}
}
}));
}
byte[]缓冲区;
使用(Stream=newfileinfo(file.OpenRead())
{
缓冲区=新字节[stream.Length];
读取(缓冲区,0,(int)stream.Length);
}
foreach(supportedCultures中的var区域性)
{
CultureInfo currentCulture=区域性;
Tasks.Add(Task.Factory.StartNew(()=>
{
var memoryStream=新的memoryStream(缓冲区);
Task serialized Task=Task.Factory.StartNew(()=>
{
返回typesManager.Load(memoryStream,currentCulture);
},TaskCreationOptions.AttachedToParent);
字符串currentOutputDirectory=null;
if(outputDirectory!=null)
{
currentOutputDirectory=outputDirectory.Replace(PlaceForCultureInFolderPath,
当前文化
.ToString());
CreateDirectory(currentOutputDirectory);
}
字符串binFile=Path.ChangeExtension(Path.GetFileName(文件),“.bin”);
字符串binPath=Path.Combine(
currentOutputDirectory??Path.GetDirectoryName(文件),
binFile);
使用(FileStream outputStream=File.OpenWrite(binPath))
{
尝试
{
新的BinaryFormatter().Serialize(outputStream,serializeTask.Result);
}
捕获(序列化异常)
{
报表编译错误(e.Message,null);
}
}
}));
}
在看不到代码或不知道任务真正在做什么的情况下,我们所能做的就是提供一些相当一般的建议和诊断
您的代码是CPU绑定还是IO绑定?(您应该能够通过查看性能监视器和运行代码时CPU的繁忙程度来判断这一点。)
如果您的代码是IO绑定的,并且在一个物理非SSD驱动器上有多个文件,那么并行工作很可能会使情况变得更糟,因为您会迫使驱动器头在整个位置上不断打点
如果您的代码是CPU受限的,那么并行化应该会有所帮助(因为这些听起来像是独立的任务)——同样,您应该能够通过先运行代码而不进行并行化,然后再进行并行化来判断这一点,在这两种情况下,请查看CPU图。您可能会认为,在串行版本中,一次只有一个CPU“忙”,而在并行版本中,所有CPU都应该忙。在看不到代码或不知道任务真正在做什么的情况下,我们所能做的就是提供一些相当一般的建议和诊断 您的代码是CPU绑定还是IO绑定?(您应该能够通过查看性能监视器和运行代码时CPU的繁忙程度来判断这一点。) 如果您的代码是IO绑定的,并且在一个物理非SSD驱动器上有多个文件,那么并行工作很可能会使情况变得更糟,因为您会迫使驱动器头在整个位置上不断打点 如果您的代码是CPU受限的,那么并行化应该会有所帮助(因为这些听起来像是独立的任务)——同样,您应该能够通过先运行代码而不进行并行化,然后再进行并行化来判断这一点,在这两种情况下,请查看CPU图。在串行版本中,一次只有一个CPU“忙”,而在并行版本中,所有CPU都应该忙。 但这并不能保证每个任务都有一个新线程,因为它使用可用的线程,只调度作业或将任务分配给任何可用的线程。因此,我建议您使用
Parallel.ForEach
var options = new ParallelOptions { MaxDegreeOfParallelism = 2 // or more };
Parallel.ForEach ( list, options, a=> { } );
但这并不能保证每个任务都有一个新线程,因为它使用可用的线程,只调度作业或将任务分配给任何可用的线程。因此,我建议您使用Parallel.ForEach
var options = new ParallelOptions { MaxDegreeOfParallelism = 2 // or more };
Parallel.ForEach ( list, options, a=> { } );
首先。不能保证TPL会对性能造成任何影响。
正如Jon所说,写入HDD会降低性能,除非操作系统缓存这些文件以供以后的顺序写入。当然,缓存大小有它的限制 第二。默认调度程序面向使用CPU核心,因此可能只有几个任务并行处理,其他任务在队列中等待。您可以通过显式设置
ParallelOptions.MaxDegreeOfParallelism
或在查询中调用WidthDegreeOfParallelism()
来更改此默认值。仍然是调度程序决定并行运行多少任务
首先,在.net中有一个关于多线程的免费软件。不能保证TPL会对性能造成任何影响。
正如Jon所说,写入HDD会降低性能,除非操作系统缓存这些文件以供以后的顺序写入。当然,缓存大小有它的限制 第二。默认调度程序面向使用CPU核心,因此可能只有几个任务并行处理,其他任务在队列中等待。您可以通过显式设置
Paralle来更改此默认值