并行多线程处理.Foreach c#.net4.5.1
本文关键字:net4 多线程处理 Foreach 并行 | 更新日期: 2023-09-27 18:00:54
我有一个IEnumerable<customClass>
对象,它大约有10-15个条目,所以不是很多,但当我尝试进行时,会遇到System.IO.FileNotFoundException
Parallel.Foreach(..some linq query.., object => { ...stuff....});
带有可枚举项。以下是我的代码,有时有效,有时无效:
IEnumerable<UserIdentifier> userIds = script.Entries.Select(x => x.UserIdentifier).Distinct();
await Task.Factory.StartNew(() =>
{
Parallel.ForEach(userIds, async userId =>
{
Stopwatch watch = new Stopwatch();
watch.Start();
_Log.InfoFormat("user identifier: {0}", userId);
await Task.Factory.StartNew(() =>
{
foreach (ScriptEntry se in script.Entries.Where(x => x.UserIdentifier.Equals(userId)))
{
// // Run the script //
_Log.InfoFormat("waiting {0}", se.Delay);
Task.Delay(se.Delay);
_Log.InfoFormat("running SelectionInformation{0}", se.SelectionInformation);
ExecuteSingleEntry(se);
_Log.InfoFormat("[====== SelectionInformation {1} ELAPSED TIME: {0} ======]", watch.Elapsed,
se.SelectionInformation.Verb);
}
});
watch.Stop();
_Log.InfoFormat("[====== TOTAL ELAPSED TIME: {0} ======]", watch.Elapsed);
});
});
当函数ExecuteSingleEntry
运行时,在该函数的深处有一个函数,它可以创建临时目录和文件。在我看来,当我运行parallel.foreach
时,该函数一次被多次调用猛烈抨击(我目前正在一次测试5,但需要处理大约10个(,并且没有创建我需要的一些文件。但是,如果我在文件创建函数中遇到了一个断点,并且每次只命中F5
,那么抛出未找到文件的异常就不会有任何问题。
因此,我的问题是,如何实现基于脚本条目中的用户id并行运行scripts.Entries
的子集,并且每个不同的用户id条目之间的延迟为1秒?
脚本条目类似于:
UserIdentifier: 141, SelectionInformation: class of stuff, Ids: list of EntryIds, Names: list of Entry Names
并且每个用户标识符可以在数组中出现1次或多次。我想同时启动所有不同的用户标识符,或多或少。然后Task
输出绑定到脚本条目的不同SelectionInformation。
scripts.Entries
是ScriptEntry
的数组,如下所示:
[DataMember]
public TimeSpan Delay { get; set; }
[DataMember]
public SelectionInformation Selection { get; set; }
[DataMember]
public long[] Ids { get; set; }
[DataMember]
public string Names { get; set; }
[DataMember]
public long UserIdentifier { get; set; }
我引用了:Parallel.ForEach vs Task.Factory.StartNew以获得
Task.Factory.StartNew(() => Parallel.Foreach({ }) )
,这样我的UI就不会锁定我的
有几个原则可以应用:
- 与
Task.Factory.StartNew
相比,更喜欢Task.Run
。我在博客上描述了为什么StartNew
是危险的;Run
是一种更安全、更现代化的替代品 - 不要将
async
lambda传递给Parallel.ForEach
。这没有道理,也不会正常工作 Task.Delay
本身不做任何事情。您必须将其await
或使用同步版本(Thread.Sleep
(
(事实上,在您的情况下,内部StartNew
是没有意义的;它已经是并行的,并且在线程池线程上运行的代码正试图在线程池上启动一个新操作,并立即异步等待它??(
应用这些原则后:
await Task.Run(() =>
{
Parallel.ForEach(userIds, userId =>
{
Stopwatch watch = new Stopwatch();
watch.Start();
_Log.InfoFormat("user identifier: {0}", userId);
foreach (ScriptEntry se in script.Entries.Where(x => x.UserIdentifier.Equals(userId)))
{
// // Run the script //
_Log.InfoFormat("waiting {0}", se.Delay);
Thread.Sleep(se.Delay);
_Log.InfoFormat("running SelectionInformation{0}", se.SelectionInformation);
ExecuteSingleEntry(se);
_Log.InfoFormat("[====== SelectionInformation {1} ELAPSED TIME: {0} ======]", watch.Elapsed,
se.SelectionInformation.Verb);
}
watch.Stop();
_Log.InfoFormat("[====== TOTAL ELAPSED TIME: {0} ======]", watch.Elapsed);
});
});