我花了大约8个多小时在网上寻求帮助,但我找不到任何东西,这里就是这样。
我正在使用Team Foundation Server和C#,我正在尝试获取工作项列表并将它们转换为我们绑定到特殊UI的通用对象。工作项目是特定日期的任务,列表大小约为30个项目,所以没有那么大的交易。
循环如下:
List<IWorkItemData> workitems = new List<IWorkItemData>();
var queryForData = Store.Query(query).Cast<WorkItem>();
if (queryForData.Count() == 0)
return workitems;
Parallel.ForEach(queryForData, (wi) =>
{
var temp = wi;
lock (workitems)
{
TFSWorkItemData tfsWorkItem = new TFSWorkItemData(temp);
workitems.Add(tfsWorkItem);
}
});
TFSWorkItemData的构造内部如下所示:
public TFSWorkItemData(WorkItem workItem)
{
this.workItem = workItem;
this.Fields = new Dictionary<string, IFieldData>();
// Add Fields
foreach (Field field in workItem.Fields)
{
TFSFieldData fieldData = new TFSFieldData
{
Value = field.Value,
OldValue = field.OriginalValue,
ReferenceName = field.ReferenceName,
FriendlyName = field.Name,
ValueType = field.FieldDefinition.SystemType
};
this.Fields.Add(field.ReferenceName, fieldData);
}
}
因此执行此操作大约需要90秒。我知道要花费很长时间才能抓住30个工作项目,所以它必须是我要做的事情才能让这花费这么长时间。我知道锁是一个性能命中,但当我删除它时,我得到一个InvalidOperationException,说该集合已被修改。当我查看此异常的详细信息时,我能找到的唯一有用信息是该集合是一个字典。奇怪的是,它并不像我工作项中的字典字典一样被修改。我班上的字典只是被添加到了,除非我遗漏了什么,否则不应该成为罪魁祸首。
请帮我弄清楚我在字典方面做错了什么。我尝试将并行foreach循环移动到workitem.Fields集合,但我似乎无法使其工作。
编辑:阅读答案的评论以回答此问题。谢谢。
答案 0 :(得分:1)
请帮我弄清楚我在字典方面做错了什么。
抛出异常是因为List<T>
不是线程安全的。
你有一个需要修改的共享资源,使用Parallel.ForEach
并没有真正帮助,因为你正在将瓶颈移到lock
,导致那里的争用,这很可能是为什么你看到性能实际上降级。线程不是一个神奇的解决方案。你需要渴望拥有尽可能多的独立工人,每个工人都能做自己的工作。
相反,您可以尝试使用PLINQ
来对内部的可枚举进行分区。由于您实际上想要在集合中投影每个元素,因此可以使用Enumerable.Select
:
var workItems = queryForData.AsParallel().Select(workItem => new TFSWorkItemData(workItem)).ToArray();
为了确定此解决方案是否实际上比顺序迭代更好,请对代码进行基准测试。永远不要假设更多的线程更快。
答案 1 :(得分:0)
我找到了另一种可能对任何试图做类似事情的人都有效的方式。
// collect all of the IDs
var itemIDs = Store.Query(query).Cast<WorkItem>().Select(wi = wi.Id).ToArray();
IWorkItemData[] workitems = new IWorkItemData[itemIDs.Length];
// then go through the list, get the complete workitem, create the wrapper,
// and finally add it to the collection
System.Threading.Tasks.Parallel.For(0, itemIDs.Length, i =>
{
var workItem = Store.GetWorkItem(itemIDs[i]);
var item = new TFSWorkItemData(workItem);
workitems[i] = item;
});
编辑:将列表更改为数组