循环内创建实例并向线程传递引用的技术疑问
问题解答
问题1:能否在每个线程中使用obj变量?它是否会保留循环内创建对象的引用?
完全可以,并且每个线程会持有对应循环迭代中创建的OtherClass实例的独立引用。
因为你在每次循环迭代的内部都重新声明了obj变量(OtherClass obj = new();),C#中这种在循环块内声明的变量,每一轮迭代都会生成一个独立的变量实例,线程的lambda表达式捕获的是当前迭代的这个专属变量,不会出现早期C#版本中循环变量共享导致的引用串位问题。所以每个线程的while循环里调用的obj.DoStuff(),都是对应那次迭代创建的对象,不会和其他线程的实例混淆。
问题2:这样操作列表会引发问题吗?是否需要在线程内添加锁,或者创建线程安全的列表访问方法?
当前代码的情况
你当前的代码里,objects.Add(obj)和threads.Add(th)都是在主线程的Create方法循环中执行的,所有线程要到循环结束后才通过foreach启动,这部分列表操作全程是单线程执行,不存在并发读写,所以不会有线程安全问题。
同时,你的线程逻辑里没有访问objects或threads这两个列表,只是操作捕获的obj实例,所以线程内部完全不需要对列表加锁。
你原本考虑的“用索引访问列表”的情况
如果改成在线程中通过索引访问objects列表(比如objects[i]),就要注意线程安全问题:
List<T>本身不是线程安全的集合,如果有多个线程同时对它进行读写操作(比如主线程继续Add,线程内做读取),会直接抛出异常或者导致数据损坏。- 这种场景下,有两种解决方式:
- 使用线程安全的集合替代
List<T>,比如如果需要按索引访问,可以用ConcurrentDictionary<int, OtherClass>;如果不需要索引,ConcurrentBag<T>这类集合也可以满足需求。 - 保留
List<T>,但在所有访问该列表的代码块(包括线程内的读取、主线程的增删)外面加上锁,示例如下:private readonly object _lockObj = new object(); // 主线程添加元素 lock(_lockObj) { objects.Add(obj); } // 线程内访问元素 lock(_lockObj) { var targetObj = objects[i]; targetObj.DoStuff(); }
- 使用线程安全的集合替代
内容的提问来源于stack exchange,提问作者TCG1
相关产品推荐
相关产品推荐

