异步编程和并行编程的性能锁定在单线程
本文关键字:编程 锁定 单线程 性能 并行 异步 | 更新日期: 2023-09-27 18:18:41
我正在为一个列表编写一些代码,该列表通过regex和DateTime元素解析字典。下面的代码确实可以工作,而且工作得相当好。然而,当我回去的时候,我注意到这种平行。每一个我都写过。我的问题很简单;使用并行是否有性能提升?如果我正在锁定线程?我觉得这似乎违背了整个目的。
同样,我觉得如果同样的想法/问题适用于异步情况,因为我正在锁定线程。
namespace StackOverflowExample
{
public class ScheduledTimeList : List<ScheduledTime>
{
public static ScheduledTimeList Parse(
Dictionary<string, string> dateTimeStrings, string startRegex,
string endRegex, object sync)
{
var scheduledTimes = new ScheduledTimeList();
Parallel.ForEach(from dt in dateTimeStrings.AsParallel()
select ParseScheduledTime(dt, startRegex, endRegex), dt =>
{
lock (sync) { scheduledTimes.Add(dt); }
});
return scheduledTimes;
}
private static ScheduledTime ParseScheduledTime(
KeyValuePair<string, string> dateTime, string startRegex,
string endRegex)
{
var startString =
dateTime.Key + " " + new Regex(startRegex).Match(dateTime.Value);
var endString =
dateTime.Key + " " + new Regex(endRegex).Match(dateTime.Value);
var startDateTime = DateTime.Parse(startString);
var endDateTime = DateTime.Parse(endString);
return new ScheduledTime(startDateTime,
endDateTime);
}
private static async Task<ScheduledTime> ParseScheduledTimeAsync(
KeyValuePair<string, string> dateTime, string startRegex,
string endRegex)
{
var startString = Task.Run(() =>
dateTime.Key + " " + new Regex(startRegex).Match(dateTime.Value).Value);
var endString = Task.Run(() =>
dateTime.Key + " " + new Regex(endRegex).Match(dateTime.Value).Value);
await Task.WhenAll(startString, endString);
var startDateTime = await Task.Run(() =>
DateTime.Parse(startString.Result));
var endDateTime = await Task.Run(() =>
DateTime.Parse(endString.Result));
return new ScheduledTime(startDateTime,
endDateTime);
}
}
}
以下是一些误解、分析问题和错误决策。
首先,如果你开始类似Parallel。ForEach或AsParallel或Tasks,这几乎是相同的,所有运行在TPL之上,然后没有一个线程,但许多或一个,这一切都取决于负载平衡,但我的观点是,锁定并不能确保所有部分将在同一线程上运行。相反,锁确保在任何给定的时间内,只有一个线程将在锁定区域内运行。当然不是一回事
现在第二个问题是,你犯了初学者常犯的错误。引入过多的并发性并没有帮助。当然,我在Parse方法中看到一个问题,在那里你首先激发内部并行块来获得结果,如果AsParallel(), PLINQ,方法,在那里它完全并发地工作,但然后你返回IEnumerable并行。ForEach,强制PLINQ将结果同步回IEnumerable。这对性能来说是一个糟糕的决定。
第三个错误是在并发代码中使用List等旧集合。并发代码在并发集合上表现最好。在这里,它意味着使用ConcurrentBag而不是List。 public class ScheduledTimeList : ConcurrentBag<ScheduledTime>
所以最后在Parse method:
中是这样的var scheduledTimes = new ScheduledTimeList();
dateTimeStrings
.AsParallel()
.ForAll(dts =>
{
var dt = ParseScheduledTime(dts, startRegex, endRegex);
scheduledTimes.Add(dt);//add's items concurently into bag,
});
并行/异步/并发编程是一件很难的事情,它真的需要大量的研究,甚至超越任务,类并行,PLINQ和async/await。
请把我的建议作为进一步学习的指针,慢慢来,一步一步来,我不想阻止它,或者听起来好像不可能理解这一点,这只是一个更广泛的问题主题…
使用并行是否有性能提升?如果我正在锁定线程?
恰恰相反。因为整个代码块是由一个锁语句控制的,所以并行运行会产生开销,然后立即导致所有线程以序列化的方式运行。