是否可以确定列表操作是否线程安全?
Is it possible to determine if a list operation is thread safe?
我正在尝试对列表 ListA 中的每个元素 X 进行比较,如果 X 的两个属性 X.Code 和 X.Rate 与任何元素 Y 的代码和速率相匹配在清单 B 中。当前的解决方案使用 LINQ 和 AsParallel 来执行这些比较(时间是一个因素,每个列表可以包含从 0 个元素到几百个元素的任意位置)。
到目前为止,AsParallel 方法似乎快得多,但我不确定这些操作是否是线程安全的。我的理解是,因为这种比较只会读取值而不是修改它们,所以这应该是安全的,但我不是 100% 有信心。在我的生产环境中释放此操作之前,我如何确定它是否是线程安全的?
这是我正在使用的代码:
var s1 = System.Diagnostics.Stopwatch.StartNew();
ListA.AsParallel().ForAll(x => x.IsMatching = ListB.AsParallel().Any(y => x.Code== y.Code && x.Rate== y.Rate));
s1.Stop();
var s2 = System.Diagnostics.Stopwatch.StartNew();
ListA.ForEach(x => x.IsMatching = ListB.Any(y => x.Code == y.Code && x.Rate== y.Rate));
s2.Stop();
目前每种方法 returns 的结果相同,但是 AsParallel() 的执行时间是普通 ForEach 的大约 1/3,所以如果有办法执行此操作,我希望能从中受益安全运行。
您的代码是线程安全的。这些列表以只读方式访问,实现并行化版本所需的隐式同步足以确保已提交任何写入。您确实修改了列表中的元素,但同样,并行操作中隐含的同步(当前线程必须等待)将确保对元素对象的任何写入在当前线程中可见。
也就是说,线程安全无关紧要,因为你做错了整件事。您正在应用蛮力 O(N^2) 算法来满足可以使用更优雅和高效的解决方案 LINQ join
:
解决的需求
var join = from x in list1
join y in list2 on new { x.Code, x.Rate } equals new { y.Code, y.Rate }
select x;
foreach (A a in join)
{
a.IsMatching = true;
}
您的代码示例不包括任何示例数据的初始化。所以我无法可靠地重现您的结果。实际上,在我的测试集中,我对 list1
和 list2
进行了相同的初始化,每个元素都具有相同的 1000 个元素(我只是将 Code
和 Rate
设置为元素的索引列表,即 0
到 999
),我发现 AsParallel()
版本 比串行版本慢 ,略多于 25%(即并行版本的 250 次迭代花费了大约 2.7 秒,而串行版本的 250 次迭代花费了大约 1.9 秒)。
但两者都没有接近 join
版本,后者在大约 60 毫秒内完成了该特定测试数据的 250 次迭代,几乎比其他两个实现快 20 倍。
我有理由相信,尽管我缺乏与您的场景相关的可比数据集,但基本结果仍然有效,并且您会发现 join
方法的使用远远不够优于您目前尝试过的任何一个选项。
我正在尝试对列表 ListA 中的每个元素 X 进行比较,如果 X 的两个属性 X.Code 和 X.Rate 与任何元素 Y 的代码和速率相匹配在清单 B 中。当前的解决方案使用 LINQ 和 AsParallel 来执行这些比较(时间是一个因素,每个列表可以包含从 0 个元素到几百个元素的任意位置)。
到目前为止,AsParallel 方法似乎快得多,但我不确定这些操作是否是线程安全的。我的理解是,因为这种比较只会读取值而不是修改它们,所以这应该是安全的,但我不是 100% 有信心。在我的生产环境中释放此操作之前,我如何确定它是否是线程安全的?
这是我正在使用的代码:
var s1 = System.Diagnostics.Stopwatch.StartNew();
ListA.AsParallel().ForAll(x => x.IsMatching = ListB.AsParallel().Any(y => x.Code== y.Code && x.Rate== y.Rate));
s1.Stop();
var s2 = System.Diagnostics.Stopwatch.StartNew();
ListA.ForEach(x => x.IsMatching = ListB.Any(y => x.Code == y.Code && x.Rate== y.Rate));
s2.Stop();
目前每种方法 returns 的结果相同,但是 AsParallel() 的执行时间是普通 ForEach 的大约 1/3,所以如果有办法执行此操作,我希望能从中受益安全运行。
您的代码是线程安全的。这些列表以只读方式访问,实现并行化版本所需的隐式同步足以确保已提交任何写入。您确实修改了列表中的元素,但同样,并行操作中隐含的同步(当前线程必须等待)将确保对元素对象的任何写入在当前线程中可见。
也就是说,线程安全无关紧要,因为你做错了整件事。您正在应用蛮力 O(N^2) 算法来满足可以使用更优雅和高效的解决方案 LINQ join
:
var join = from x in list1
join y in list2 on new { x.Code, x.Rate } equals new { y.Code, y.Rate }
select x;
foreach (A a in join)
{
a.IsMatching = true;
}
您的代码示例不包括任何示例数据的初始化。所以我无法可靠地重现您的结果。实际上,在我的测试集中,我对 list1
和 list2
进行了相同的初始化,每个元素都具有相同的 1000 个元素(我只是将 Code
和 Rate
设置为元素的索引列表,即 0
到 999
),我发现 AsParallel()
版本 比串行版本慢 ,略多于 25%(即并行版本的 250 次迭代花费了大约 2.7 秒,而串行版本的 250 次迭代花费了大约 1.9 秒)。
但两者都没有接近 join
版本,后者在大约 60 毫秒内完成了该特定测试数据的 250 次迭代,几乎比其他两个实现快 20 倍。
我有理由相信,尽管我缺乏与您的场景相关的可比数据集,但基本结果仍然有效,并且您会发现 join
方法的使用远远不够优于您目前尝试过的任何一个选项。