使用Task.Run并行执行时,GetConnectionsConnectedPosition返回结果异常
问题根源分析
你遇到的X值异常是典型的线程不安全问题:静态方法GetConnectionsConnectedPosition内部使用了共享的静态变量来维护X值的状态(比如当前起始偏移、累计增量等),当多个Task并行调用时,线程会无同步地修改这个共享变量,导致后执行的任务覆盖了前一个任务的X值上下文,最终所有任务都复用了最后一个任务的起始X值。
Thread.Sleep(20)能临时解决,只是因为它强制让线程执行顺序变得近似串行,避开了并发修改的冲突,但这完全是依赖时间的不可靠方案,在高负载或不同性能的处理器上必然会再次出现问题。
可靠解决方案
1. 移除静态方法内的共享可变状态(最优方案)
把静态方法中用于维护X值的静态变量,改成方法内部的局部变量,让每个方法调用都拥有独立的状态副本,从根源避免线程间的状态干扰。
示例修正:
// 错误的线程不安全实现(存在共享静态变量) public static class NeuronUtils { private static int _sharedCurrentX; // 所有线程共享的可变状态 public static List<Point> GetConnectionsConnectedPosition(int startX, int startY, int connectionCount) { _sharedCurrentX = startX; var positions = new List<Point>(); for (int i = 0; i < connectionCount; i++) { positions.Add(new Point(_sharedCurrentX, startY + i * 8)); _sharedCurrentX += 12; // 并发时会被多个线程同时修改 } return positions; } }
// 修正后的线程安全实现(用局部变量替代共享静态变量) public static class NeuronUtils { public static List<Point> GetConnectionsConnectedPosition(int startX, int startY, int connectionCount) { int currentX = startX; // 每个调用独立维护自己的X状态 var positions = new List<Point>(); for (int i = 0; i < connectionCount; i++) { positions.Add(new Point(currentX, startY + i * 8)); currentX += 12; } return positions; } }
2. 若必须保留共享状态,添加线程同步(次优方案)
如果静态方法必须依赖某个全局共享的可变状态(比如全局配置的偏移量),则需要用同步机制保护对该状态的访问,避免并发冲突。常用的方式是lock块,或Interlocked类做原子操作,但这会牺牲并行性能,因为线程会排队执行:
public static class NeuronUtils { private static int _sharedGlobalOffset; private static readonly object _lockObj = new object(); // 同步锁对象 public static List<Point> GetConnectionsConnectedPosition(int startX, int startY, int connectionCount) { int currentX; // 用lock保护共享变量的读取/修改 lock (_lockObj) { currentX = startX + _sharedGlobalOffset; _sharedGlobalOffset += connectionCount * 12; } var positions = new List<Point>(); for (int i = 0; i < connectionCount; i++) { positions.Add(new Point(currentX, startY + i * 8)); currentX += 12; } return positions; } }
验证方法
修改完成后,移除Thread.Sleep(20),用多Task并行调用测试:
public NeuronConnectionsInfo() { var tasks = new List<Task<List<Point>>>(); tasks.Add(Task.Run(() => NeuronUtils.GetConnectionsConnectedPosition(100, 50, 8))); tasks.Add(Task.Run(() => NeuronUtils.GetConnectionsConnectedPosition(200, 150, 6))); tasks.Add(Task.Run(() => NeuronUtils.GetConnectionsConnectedPosition(300, 250, 10))); var allPositions = Task.WhenAll(tasks).Result; // 检查每个任务返回的X值是否从各自的startX开始 }
内容的提问来源于stack exchange,提问作者Germán Gasset
相关产品推荐
相关产品推荐

