我正在尝试通过遍历List<TestRequest>.
来创建xml
为了获得更好的性能,我正在尝试Parallel.ForEach
进行循环,因为列表中有成千上万条记录,但是我没有得到一致的数据
在xml
中,有时在附加到字符串生成器的xml
字符串中会出现截断,有时会出现数据不匹配的情况。
下面是代码
public class Program
{
static void Main(string[] args)
{
List<TestRequest> ids = new List<TestRequest>();
Random rnd = new Random();
int id = rnd.Next(1, 12345);
for (int i = 1; i < 1000; i++)
{
var data = new TestRequest();
data.dataId = id;
ids.Add(data);
}
var xmlData = GetIdsinXML(ids);
}
private static string GetIdsinXML(List<TestRequest> Ids)
{
var sb = new StringBuilder();
sb.Append("<ROOT>");
Parallel.ForEach(Ids, id =>
{
sb.Append("<Row");
sb.Append(" ID='" + id.dataId + "'");
sb.Append("></Row>");
}
);
sb.Append("</ROOT>");
return sb.ToString();
}
}
public class TestRequest
{
public int dataId { get; set; }
}
这是使用Parallel.ForEach的正确方法吗?
请帮助。 谢谢!
答案 0 :(得分:2)
这是并行执行所需操作的最简单方法:
public class TestRequest
{
public int dataId { get; set; }
public string ToXml() => $"<row id=\"{dataId}\"/>";
}
class Program
{
static void Main(string[] args)
{
const int n = 10000000;
List<TestRequest> ids = new List<TestRequest>();
Random rnd = new Random();
for (int i = 1; i<n; i++)
{
var data = new TestRequest
{
dataId=rnd.Next(1, 12345)
};
ids.Add(data);
}
Stopwatch sw = Stopwatch.StartNew();
var xml = GetIdsinXML(ids);
sw.Stop();
double time = sw.Elapsed.TotalSeconds;
File.WriteAllText("result.xml", xml);
Process.Start("result.xml");
var output = $"Size={n} items, Time={time} sec, Speed={n/time/1000000} M/sec";
#if DEBUG
Debug.WriteLine(output);
#else
Console.WriteLine(output);
#endif
}
static string GetIdsinXML(List<TestRequest> requests)
{
// parallel query
var list = requests.AsParallel().Select((item) => item.ToXml());
// or sequential below:
// var list = requests.Select((item) => item.ToXml());
return $"<root>\r\n{string.Join("\r\n", list)}\r\n</root>";
}
}
在糟糕的计算机上,没有.AsParallel()
语句,按顺序执行,每秒可以执行约1,600,000次操作。有了并行语句,该速度将跃升至每秒2100,000次操作。
注意:我已将SpringBuilder
替换为内置方法string.Join(string, IEnumerable list)
,Microsoft已对其进行了相当优化。有趣的一点是,“调试”构建与“发布”构建一样快甚至更快。走吧。
答案 1 :(得分:1)
使用PLINQ,您可以执行以下操作。
我不确定您是否真的需要这个。我只是为了回答而提出。
您的代码不起作用,因为StringBuilder不是线程安全的,并且您的操作也不是原子的,这意味着您的代码具有竞争条件。
sb.Append("<Row");
sb.Append(" ID='" + id.dataId + "'");
sb.Append("></Row>");
例如,一个线程可能执行第1行,而另一个线程紧随其后执行第3行。您将拥有<Row></Row>
。这种比赛状态一直发生,最终结果是胡言乱语。
解决此问题的一种方法是在不同的线程上使用不同的StringBuilder,最后按顺序附加这些构建器的结果。
如果正在运行的线程执行的任务非常轻松并且很快完成,那么并行执行只会减慢程序速度。
return Ids.AsParallel()
.Select((id, index) => (id, index))
.GroupBy(x => x.index%Environment.ProcessorCount, x => x.id, (k, g) => g)
.Select(g =>
{
var sb = new StringBuilder();
foreach (var id in g)
{
sb.Append("<Row");
sb.Append(" ID='" + id.dataId + "'");
sb.Append("></Row>");
}
return sb.ToString();
})
.AsSequential()
.Aggregate(new StringBuilder("<ROOT>"), (a, b) => a.Append(b))
.Append("</ROOT>").ToString();
测量性能,看它是否确实有改善。如果不是不并行的话。