相关疑难解决方法(0)

参考分配线程安全吗?

我正在C#中构建一个多线程缓存,它将包含一个Car对象列表:

public static IList<Car> Cars {get; private set;}
Run Code Online (Sandbox Code Playgroud)

我想知道在没有锁定的情况下更改线程中的引用是否安全?

例如

private static void Loop()
{
  while (true)
  {
    Cars = GetFreshListFromServer();
    Thread.Sleep(SomeInterval);
  }
}
Run Code Online (Sandbox Code Playgroud)

基本上,它归结为是否为汽车分配新的参考是原子的还是不是我想的.

如果不是,我显然必须为我的汽车使用私人领域,并锁定获取和设置.

c# thread-safety

36
推荐指数
1
解决办法
1万
查看次数

记忆障碍发生器

阅读Joseph Albahari的线程教程,以下内容被提及为内存障碍的生成器:

  • C#的lock陈述(Monitor.Enter/ Monitor.Exit)
  • Interlocked班上的所有方法
  • 使用线程池的异步回调 - 包括异步委托,APM回调和任务延续
  • 设置和等待信令构造
  • 任何依赖于信令的东西,例如启动或等待任务

此外,Hans Passant和Brian Gideon 补充了以下内容(假设其中没有一个已经符合以前的类别之一):

  • 启动或唤醒线程
  • 上下文切换
  • Thread.Sleep()

我想知道这个清单是否完整(如果完整清单甚至可以实际制作)

编辑补充建议:

  • 易失性(读数意味着获取围栏,写作意味着释放围栏)

c# memory-barriers

24
推荐指数
2
解决办法
4193
查看次数

volatile和readonly应该互相排斥吗?

假设我正在设计一个包装内部集合的线程安全类:

public class ThreadSafeQueue<T>
{
    private readonly Queue<T> _queue = new Queue<T>();

    public void Enqueue(T item)
    {
        lock (_queue)
        {
            _queue.Enqueue(item);
        }
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

基于我的另一个问题,上面的实现是错误的,因为当它的初始化与其使用同时执行时可能会出现种族危险:

ThreadSafeQueue<int> tsqueue = null;

Parallel.Invoke(
    () => tsqueue = new ThreadSafeQueue<int>(),
    () => tsqueue?.Enqueue(5));
Run Code Online (Sandbox Code Playgroud)

上面的代码是可接受的非确定性的:该项目可能会或可能不会入队.但是,在当前的实现中,它也被破坏了,并且可能引起不可预测的行为,例如抛出IndexOutOfRangeException,NullReferenceException多次将相同的项目入队,或者陷入无限循环.这是因为Enqueue调用可能将新实例分配给局部变量tsqueue之后但内部_queue字段的初始化完成(或似乎完成)之前运行.

Per Jon Skeet:

在将对新对象的引用分配给实例之前,Java内存模型不能确保构造函数完成.Java内存模型经历了1.5版的重新加工,但是在没有volatile变量的情况下,双重检查锁定仍然被破坏(如在C#中).

可以通过向构造函数添加内存屏障来解决此种族危险:

    public ThreadSafeQueue()
    {
        Thread.MemoryBarrier();
    }
Run Code Online (Sandbox Code Playgroud)

同样,通过使字段变化可以更简洁地解决:

    private volatile readonly Queue<T> _queue = new …
Run Code Online (Sandbox Code Playgroud)

.net c# concurrency multithreading volatile

6
推荐指数
1
解决办法
449
查看次数