多异步方法并发调用同一函数引发随机错误排查求助
并发调用异步任务时随机异常的排查与解决
问题背景
同步执行流程完全正常,但通过Task.Run启动多个异步任务并发调用addGroupStringToDictionary函数时,会出现随机错误,且每次错误触发情况不固定。
相关代码
异步任务定义
//Some other tasks before Task<bool> taskIfcQuantityArea = Task.Run<bool>(() => { return this.addGroupStringToDictionary("IfcQuantityArea"); }); Task<bool> taskIfcQuantityLength = Task.Run<bool>(() => { return this.addGroupStringToDictionary("IfcQuantityLength"); }); Task<bool> taskIfcSiUnit = Task.Run<bool>(() => { return addGroupStringToDictionary("IfcSiUnit"); }); Task<bool> taskIfcPropertySingleValue = Task.Run<bool>(() => { return addGroupStringToDictionary("IfcPropertySingleValue"); }); //uses IfcPerson, IfcOrganization Task<bool> taskIfcPersonAndOrganization = Task.Run<bool>(() => { return addGroupStringToDictionary("IfcPersonAndOrganization"); }); //uses IfcOrganization Task<bool> taskIfcApplication = Task.Run(async () => { await taskIfcSiUnit; return addGroupStringToDictionary("IfcApplication"); }); //uses IfcSiUnit Task<bool> taskIfcMeasureWithUnit = Task.Run(async () => { await taskIfcSiUnit; return addGroupStringToDictionary("IfcMeasureWithUnit"); }); //some other tasks after.
addGroupStringToDictionary函数
private bool addGroupStringToDictionary(string typeName) { //int processCount = await Task.Run<int>(() => //{ GroupedListStrings groupElt = this.listGrouppedStrings.FirstOrDefault(x => x.Type == typeName.ToUpper()); if (groupElt != null) { List<string> listStringInGroup = groupElt.ListStrings; foreach (string line in listStringInGroup) { try { if(typeName== "IfcLocalPlacement($") { typeName = "IfcLocalPlacement"; } var type = Type.GetType("Ifc."+typeName); if (typeName == "IfcPropertySingleValue" || typeName == "IfcDirection" || typeName == "IfcSiUnit" || typeName == "IfcQuantityLength" || typeName == "IfcQuantityArea" || typeName == "IfcQuantityVolume" || typeName == "IfcQuantityWeight") { try { object instance = Activator.CreateInstance(type, line); this.addToListDictionary((IfcElement)instance); } catch { } } else if (typeName == "IfcOpeningElement") { try { object instance = Activator.CreateInstance(type, line, this.listDictionaries, this.DictionaryBolts); this.addToListDictionary((IfcElement)instance); } catch { } } else { try { object instance = Activator.CreateInstance(type, line, this.listDictionaries); this.addToListDictionary((IfcElement)instance); } catch { } } } catch { this.addError(line); } } this.listGrouppedStrings.Remove(groupElt); this.reportProgressImport(); } //return 100; //}); this.reportProgressImport(); return true; }
addToListDictionary函数
private void addToListDictionary(IfcElement elt) { if(elt.ErrorFound) { this.listReadButError.Add(elt); return; } string type = elt.GetType().ToString(); if (elt is IfcRepere) { type = "Ifc.IfcRepere"; } else if (elt is IfcRepereType) { type = "Ifc.IfcRepereType"; } else if (elt is IfcPhysicalSimpleQuantity) { type = "Ifc.IfcPhysicalSimpleQuantity"; } else if (elt is IfcProfileDef) { type = "Ifc.IfcProfileDef"; } else if (elt is IfcGeometricRepresentationContext) { type = "Ifc.IfcGeometricRepresentationContext"; } GroupDictionary group = this.ListDictionaries.FirstOrDefault(x => x.Name == type); if(group==null) { group = new GroupDictionary { Name = type }; this.ListDictionaries.Add(group); } group.ListElements[elt.ID] = elt; if (elt is IfcMechanicalFastener) { IfcMechanicalFastener bolt = (IfcMechanicalFastener)elt; this.DictionaryBolts[bolt.Tag] = bolt; } else if(elt is IfcProject) { this.listProjects.Add((IfcProject)elt); } else if(elt is IfcElementAssembly ifcAss) { this.DictionaryIfcElementAssemblies[ifcAss.Key] = ifcAss; } }
相关成员定义
private List<GroupDictionary> listDictionaries = new List<GroupDictionary>(); public List<GroupDictionary> ListDictionaries { get { return this.listDictionaries; } set { this.listDictionaries = value; } }
GroupDictionary类
public class GroupDictionary { string name { get; set; } public string Name { get { return this.name; } set { this.name = value; } } public ConcurrentDictionary<int, IfcElement> ListElements = new ConcurrentDictionary<int, IfcElement>(); public GroupDictionary() { } }
原因确认
你的猜测完全正确,问题根源在于非线程安全的共享集合被多线程并发操作:
listGrouppedStrings:List<T>本身不支持线程安全,并发执行FirstOrDefault查询和Remove删除操作时,会导致枚举器失效、元素丢失或索引错乱。ListDictionaries:多线程同时执行FirstOrDefault查询和Add添加操作,可能出现重复添加相同GroupDictionary的情况,或查询后添加前被其他线程修改导致逻辑错误。listReadButError、listProjects等List<T>集合,同样存在并发添加的线程安全问题。
解决方案
1. 替换为线程安全集合
将非线程安全集合替换为System.Collections.Concurrent命名空间下的线程安全集合,从根源避免并发问题:
ListDictionaries替换为ConcurrentDictionary:
对应修改private ConcurrentDictionary<string, GroupDictionary> dictDictionaries = new ConcurrentDictionary<string, GroupDictionary>(); public ConcurrentDictionary<string, GroupDictionary> DictDictionaries { get { return dictDictionaries; } }addToListDictionary中的分组获取逻辑:// 自动处理不存在键的情况,线程安全 GroupDictionary group = dictDictionaries.GetOrAdd(type, key => new GroupDictionary { Name = key });listGrouppedStrings替换为ConcurrentDictionary:
修改private ConcurrentDictionary<string, GroupedListStrings> dictGrouppedStrings = new ConcurrentDictionary<string, GroupedListStrings>();addGroupStringToDictionary中的获取和删除逻辑:// 原子性移除元素,避免并发冲突 if (dictGrouppedStrings.TryRemove(typeName.ToUpper(), out var groupElt)) { // 处理groupElt的业务逻辑 }- 对于
listReadButError、listProjects等集合,若无需顺序要求可替换为ConcurrentBag<T>,若需顺序则用lock保护操作。
2. 使用锁保护临界区
如果无法替换集合类型,在访问共享集合的代码块上添加锁,确保同一时间只有一个线程操作:
listGrouppedStrings操作加锁:private readonly object listGrouppedLock = new object(); // ... GroupedListStrings groupElt; lock(listGrouppedLock) { groupElt = this.listGrouppedStrings.FirstOrDefault(x => x.Type == typeName.ToUpper()); if(groupElt != null) { this.listGrouppedStrings.Remove(groupElt); } } if(groupElt != null) { // 后续处理逻辑(已移除集合,无需锁) }ListDictionaries操作加锁:
注意:锁对象应为只读私有字段,避免嵌套锁,防止死锁。private readonly object dictListLock = new object(); // ... GroupDictionary group; lock(dictListLock) { group = this.ListDictionaries.FirstOrDefault(x => x.Name == type); if(group==null) { group = new GroupDictionary { Name = type }; this.ListDictionaries.Add(group); } } group.ListElements[elt.ID] = elt;
3. 优化任务依赖逻辑
目前部分任务依赖taskIfcSiUnit,需确保依赖关系正确,但即使有依赖,其他无依赖任务仍会并发执行,因此共享资源的线程安全处理仍是核心。
内容的提问来源于stack exchange,提问作者Siegfried.V
相关产品推荐
相关产品推荐

