Tag: 内存屏障

线程安全使用锁定助手(关于内存障碍)

通过锁定助手,我指的是一次性对象,通过using语句可以实现锁定。 例如,考虑Jon Skeet的MiscUtil中 SyncLock类的典型用法: public class Example { private readonly SyncLock _padlock; public Example() { _padlock = new SyncLock(); } public void ConcurrentMethod() { using (_padlock.Lock()) { // Now own the padlock – do concurrent stuff } } } 现在,请考虑以下用法: var example = new Example(); new Thread(example.ConcurrentMethod).Start(); 我的问题是这个 – 因为example是在一个线程上创建的,而ConcurrentMethod是在另一个线程上调用的,所以ConcurrentMethod的线程无法忽略_padock在构造函数中的赋值(由于线程缓存/读写重新排序),因此抛出NullReferenceException (在_padLock本身)? 我知道使用Monitor / lock会带来内存障碍,但是当使用这些锁定助手时,我无法理解为什么会有这样的障碍。 在这种情况下,据我所知,构造函数必须被修改: […]

线程同步。 完全锁定如何使内存访问“正确”?

首先,我知道lock{}是Monitor类的合成糖。 (哦, 语法糖) 我正在玩简单的multithreading问题,并发现无法完全理解锁定一些任意WORD的内存如何保护整个其他内存不被缓存的寄存器/ CPU缓存等。使用代码示例来解释我所说的内容更容易: for (int i = 0; i < 100 * 1000 * 1000; ++i) { ms_Sum += 1; } 最后, ms_Sum将包含100000000 ,这当然是预期的。 现在我们年龄将执行相同的周期,但在2个不同的线程上,上限减半。 for (int i = 0; i < 50 * 1000 * 1000; ++i) { ms_Sum += 1; } 由于没有同步,我们得到不正确的结果 – 在我的4核机器上,它是随机数近52 388 219 ,略大于100 000 000一半。 如果我们包含ms_Sum += […]

线程安全类应该在其构造函数的末尾有一个内存屏障吗?

在实现一个用于线程安全的类时,我是否应该在其构造函数的末尾包含一个内存屏障,以确保在访问它们之前已完成任何内部结构的初始化? 或者消费者有责任在将实例提供给其他线程之前插入内存屏障吗? 简化问题 : 由于初始化和线程安全类的访问之间缺乏内存屏障,下面的代码中是否存在种族风险可能会导致错误行为? 或者线程安全类本身是否应该防止这种情况? ConcurrentQueue queue = null; Parallel.Invoke( () => queue = new ConcurrentQueue(), () => queue?.Enqueue(5)); 请注意,程序可以接受任何内容,如果第二个委托在第一个委托之前执行,则会发生这种情况。 (null条件运算符?.在这里防止NullReferenceException 。)但是,程序抛出IndexOutOfRangeException , NullReferenceException ,多次排队5 ,陷入无限循环或执行任何其他操作都不应该是可接受的。由内部结构的种族危害引起的怪异事物。 详细问题 : 具体来说,假设我正在为队列实现一个简单的线程安全包装器。 (我知道.NET已经提供了ConcurrentQueue ;这只是一个例子。)我可以写: public class ThreadSafeQueue { private readonly Queue _queue; public ThreadSafeQueue() { _queue = new Queue(); // Thread.MemoryBarrier(); // Is this line required? } […]