是否存在并行与线程安全计算相结合的模式?
需要计算第一步将从并行中受益的结果,第二步是并行结果的串行过程。
一个选项是运行并行并将输出保存到集合,然后连续处理集合,我有这个工作。问题在于内存管理,因为集合可能非常大。
以下是串行版本。基本上我想并行TableQueryGetRowKeys并以线程安全的方式使用该结果。试图只是平行于for并锁定最终结果,但rowKeys可能会关闭。尝试聚合,但我无法弄清楚如何将集合传递给聚合,更不用说在聚合中执行线程安全的交叉。
IEnumerable<string> finalResults = null;
if (partitionKey.Length == 0) return finalResults;
object lockObject = new object();
finalResults = TableQueryGetRowKeys(partitionKey[0], 0);
HashSet<string> rowKeys;
for(int i = 1; i < partitionKey.Length; i++)
{
// IO operation to Azure Table Storage against the PartitionKey
// so very amenable to parallel
rowKeys = TableQueryGetRowKeys(partitionKey[i]);
// a memory and CPU operation
// this should be much faster than TableQueryGetRowKeys
// going parallel and wrapping this in a lock did not properly synch rowKeys
finalResults = finalResults.Intersect(rowKeys);
}
return finalResults;
答案 0 :(得分:2)
假设TableQueryGetRowKeys
是线程安全的:
var final = partitionKey.AsParallel()
// By returning AsParallel we can get parallel intersect
.Select(k => TableQueryGetRowKeys(k).AsParallel())
.Aggregate((x, y) => x.Intersect(y));
// Using fake-ish data I see about a 30% speed-up on a 4-core machine:
// static HashSet<string> TableQueryGetRowKeys(string prefix)
// {
// // Simulate 1s of IO round-trip
// if (useSleep) Thread.Sleep(1000);
//
// return new HashSet<string>(
// Enumerable.Range(0, 500)
// .Select(_ => random.Value.Next(0, 500).ToString()));
// }
这种算法以逐步的方式工作:
partitionKey.AsParallel()
将常规IEnumerable<string>
变为ParallelQuery<string>
,允许并行处理序列。ParallelEnumerable.Select
用于并行呼叫TableQueryGetRowKeys
。TableQueryGetRowKeys
的结果都会使用ParallelQuery<T>
包裹在AsParallel()
中。ParallelEnumerable.Intersect
用作TableQueryGetRowKeys
返回的每个“并行启用”枚举的聚合函数。实际上,可以使用 in serial 来删除AsParallel
来取代之前的代码,如下所示:
var serialEquivalent = partitionKey.Select(k => TableQueryGetRowKeys(k))
.Aggregate((x,y) => x.Intersect(y));
当您查看实施中的肉和土豆时,您可以“说服”自己这相当于您的方法:
IEnumerable<string> results = SomeMethod(0);
for (int ii = 1; ii < count; ++ii)
{
results = results.Intersect(SomeMethod(ii));
}
使用+
代替Intersect
重写上述内容:
int results = SomeMethod(0);
for (int ii = 1; ii < count; ++ii)
{
results = results + SomeMethod(ii);
}
现在很清楚,可以使用Intersect
代替其他更“常见”的聚合函数(例如数学运算符)。