Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#更新对象引用和多线程_C#_Multithreading_Reference_Garbage Collection_Thread Safety - Fatal编程技术网

C#更新对象引用和多线程

C#更新对象引用和多线程,c#,multithreading,reference,garbage-collection,thread-safety,C#,Multithreading,Reference,Garbage Collection,Thread Safety,读了这么多关于如何做的书后,我很困惑 下面是我想做的: 我有一个保存各种信息的数据结构/对象。我踏着数据结构,好像它是不可变的。每当我需要更新信息时,我都会制作一个DeepCopy并对其进行更改。然后交换旧对象和新创建的对象 现在我不知道如何把每件事都做好 让我们从读卡器/消费者线程的角度来看它 MyObj temp = dataSource; var a = temp.a; ... // many instructions var b = temp.b; .... 据我所知,阅读参考文献是原

读了这么多关于如何做的书后,我很困惑

下面是我想做的: 我有一个保存各种信息的数据结构/对象。我踏着数据结构,好像它是不可变的。每当我需要更新信息时,我都会制作一个DeepCopy并对其进行更改。然后交换旧对象和新创建的对象

现在我不知道如何把每件事都做好

让我们从读卡器/消费者线程的角度来看它

MyObj temp = dataSource;
var a = temp.a;
... // many instructions
var b = temp.b;
....
据我所知,阅读参考文献是原子的。因此,我不需要任何volatile或锁定来将数据源的当前引用分配给temp。但是垃圾收集呢。据我所知,GC有某种引用计数器,可以知道何时释放内存。所以,当另一个线程恰好在数据源被分配给temp时更新数据源时,GC是否会增加右侧内存块上的计数器? 另一件事是编译器/CLR优化。我将数据源分配给temp,并使用temp访问数据成员。CLR做什么?它是真的复制了dataSource还是优化器只是使用dataSource访问.a和.b?让我们假设temp.a和temp.b之间有很多指令,因此对temp/dataSource的引用不能保存在CPU寄存器中。那么temp.b真的是temp.b吗?或者它是针对dataSource.b进行优化的,因为复制到temp是可以优化的。如果另一个线程更新数据源以指向新对象,这一点尤其重要

我真的需要volatile、lock、readwriterlocksim、Thread.MemoryBarrier或其他东西吗? 对我来说重要的是,我想确保temp.a和temp.b访问旧的数据结构,即使另一个线程将数据源更新为另一个新创建的数据结构。我从不更改现有结构中的数据。更新总是通过创建副本、更新数据,然后更新对datastructre新副本的引用来完成


也许还有一个问题。如果我不使用volatile,那么需要多长时间才能让所有CPU上的所有内核看到更新的引用


说到挥发性,请看这里:


我做了一些测试程序:

namespace test1 {
  public partial class Form1 : Form {
    public Form1() { InitializeComponent(); }

    Something sharedObj = new Something();

    private void button1_Click(object sender, EventArgs e) {
      Thread t = new Thread(Do);          // Kick off a new thread
      t.Start();                               // running WriteY()

      for (int i = 0; i < 1000; i++) {
        Something reference = sharedObj;

        int x = reference.x; // sharedObj.x;
        System.Threading.Thread.Sleep(1);
        int y = reference.y; // sharedObj.y;

        if (x != y) {
          button1.Text = x.ToString() + "/" + y.ToString();
          Update();
        }
      }
    }

    private void Do() {
      for (int i = 0; i < 1000000; i++) {
        Something someNewObject = sharedObj.Clone(); // clone from immutable
        someNewObject.Do();
        sharedObj = someNewObject; // atomic
      }
    }
  }

  class Something {
    public Something Clone() { return (Something)MemberwiseClone(); }
    public void Do() { x++; System.Threading.Thread.Sleep(0); y++; }
    public int x = 0;
    public int y = 0;
  }
}
namespace test1{
公共部分类Form1:Form{
公共表单1(){InitializeComponent();}
Something sharedObj=新事物();
私有无效按钮1\u单击(对象发送者,事件参数e){
线程t=新线程(Do);//启动一个新线程
t、 Start();//正在运行WriteY()
对于(int i=0;i<1000;i++){
参考某物=sharedObj;
int x=reference.x;//sharedObj.x;
系统线程线程睡眠(1);
int y=reference.y;//sharedObj.y;
如果(x!=y){
button1.Text=x.ToString()+“/”+y.ToString();
更新();
}
}
}
私人无效Do(){
对于(int i=0;i<1000000;i++){
someNewObject=sharedObj.Clone();//从不可变对象克隆
someNewObject.Do();
sharedObj=someNewObject;//原子
}
}
}
分类{
public Something Clone(){return(Something)MemberwiseClone();}
public void Do(){x++;System.Threading.Thread.Sleep(0);y++;}
公共整数x=0;
公共整数y=0;
}
}
在Button1\u中,单击有一个for循环,在for循环中,我使用直接“shareObj”和临时创建的“reference”访问数据结构/对象。使用引用足以确保“var a”和“var b”使用来自同一对象的值进行初始化

我唯一不明白的是,为什么“Something reference=sharedObj;”没有被优化掉,“int x=reference.x;”没有被“int x=sharedObj.x;”取代

编译器,jitter,怎么知道不优化这个呢?或者,对象在C#中暂时没有优化过

但最重要的是:我的示例之所以按预期运行,是因为它是正确的,还是只是意外地按预期运行

据我所知,阅读参考文献是原子的

对。但这是一个非常有限的财产。这意味着阅读参考资料会起作用;你永远不会得到半个旧引用的比特与半个新引用的比特混合,从而导致一个不工作的引用。如果有一个同时发生的变化,它对你是否得到旧的或新的参考没有任何承诺(这样的承诺意味着什么?)

因此,我不需要任何volatile或锁定来将数据源的当前引用分配给temp

也许吧,尽管在某些情况下这可能会有问题

但是垃圾收集呢。据我所知,GC有某种引用计数器,可以知道何时释放内存

不对。.NET垃圾收集中没有引用计数

如果存在对对象的静态引用,则该对象不符合回收条件

如果存在对对象的活动本地引用,则该对象不可用于回收

如果对象的字段中存在不符合回收条件的对象引用,则该对象也不符合回收条件(递归)

这里不算。要么存在禁止开垦的积极有力的参考,要么没有

这有很多非常重要的影响。这里的相关性在于,永远不会有任何不正确的引用计数,因为没有引用计数。强引用不会消失在你的脚下

另一件事是编译器/CLR优化。我将数据源分配给temp,并使用temp访问数据成员。CLR做什么?它是真的复制了dataSource还是优化器只是使用dataSource访问.a和.b

这取决于
dataSource
temp
是什么,取决于它们是否是本地的,以及
var a = dataSource.a;
... // many instructions
var b = dataSource.b;
/* Thread A */
dataSource = null;

/* Some time has passed */

/* Thread B */
var isNull = dataSource == null;