我经常听说在.NET 2.0内存模型中,写入始终使用释放围栏.这是真的?这是否意味着即使没有明确的内存屏障或锁定,也不可能在不同于创建它的线程上观察部分构造的对象(仅考虑引用类型)?我显然排除了构造函数泄漏this引用的情况.
例如,假设我们有不可变的引用类型:
public class Person
{
public string Name { get; private set; }
public int Age { get; private set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
Run Code Online (Sandbox Code Playgroud)
是否可以使用以下代码观察除"John 20"和"Jack 21"之外的任何输出,例如"null 20"或"Jack 0"?
// We could make this volatile to freshen the read, but I don't want
// to complicate the core of the question.
private Person person;
private void Thread1()
{
while (true)
{
var personCopy = person; …Run Code Online (Sandbox Code Playgroud) 根据MSDN:
volatile关键字表示某个字段可能被同时执行的多个线程修改.声明为volatile的字段不受编译器优化的约束,这些优化假定由单个线程进行访问.这可确保始终在字段中显示最新值.
请注意最后一句:
这可确保始终在字段中显示最新值.
但是,此关键字存在问题.
我已经读过它可以改变指令的顺序:
Run Code Online (Sandbox Code Playgroud)First instruction Second instruction Can they be swapped? Read Read No Read Write No Write Write No Write Read Yes! <----
这意味着John为一个易变的字段设置了一个值,后来 Paul想要阅读该字段,Paul正在获得旧的价值!
这是怎么回事?这不是主要的工作吗?
我知道还有其他解决方案,但我的问题是关于volatile关键字.
我(作为程序员)是否需要阻止使用此关键字 - 因为这种奇怪的行为?
我一直在阅读,以Full fences防止任何类型的指令重新排序或缓存围栏(通过memoryBarrier)
然后我读到了volatile 哪些会产生"半围栏":
volatile关键字指示编译器在每次从该字段读取时生成一个获取栅栏,并在每次写入该字段时生成一个释放栅栏.
acquire-fence
获取栅栏可防止其他读/写在栅栏前移动;
release-fence
释放栅栏可防止在栅栏后移动其他读/写.
有人可以用简单的英语向我解释这两句话吗?
(围栏在哪里?)
在这里得到一些答案后 - 我已经制作了一幅可以帮助每个人的图画 - 我想.
是否有可能在C#中抢占构造函数?
例如,考虑代码:
public class A
{
public bool ready = true;
public A()
{
ready = false; // Point #1
// Other initialization stuff
ready = true; // Point #2
}
}
Run Code Online (Sandbox Code Playgroud)
在代码中的其他地方,两个线程可以访问类型A的变量,第一个线程调用在#1点抢占的构造函数.然后第二个线程测试ready并发现它仍然是真的因此它做了一些坏事.
这种情况可能吗?
进一步来说:
lock构造函数中的同步代码?众所周知,与Java的易失性不同,.NET的版本允许使用来自另一个位置的以下易失性读取来重新排序易失性写入.当它是一个问题时 MemoryBarier,建议放在它们之间,或者Interlocked.Exchange可以用来代替volatile写.
它可以工作,但MemoryBarier在高度优化的无锁代码中使用时可能成为性能杀手.
我想了一下,想出了一个主意.我希望有人告诉我,我是否采取了正确的方式.
所以,这个想法如下:
我们希望防止这两种访问之间的重新排序:
volatile1 write
volatile2 read
Run Code Online (Sandbox Code Playgroud)
从.NET MM我们知道:
1) writes to a variable cannot be reordered with a following read from
the same variable
2) no volatile accesses can be eliminated
3) no memory accesses can be reordered with a previous volatile read
Run Code Online (Sandbox Code Playgroud)
为了防止写入和读取之间不必要的重新排序,我们从刚刚写入的变量中引入了一个虚拟易失性读取:
A) volatile1 write
B) volatile1 read [to a visible (accessible | potentially shared) location]
C) volatile2 read
Run Code Online (Sandbox Code Playgroud)
在这种情况下,B不能用A重新排序,因为它们都访问相同的变量, C不能用B重新排序,因为两个易失性读取不能相互重新排序,并且传递C …
我有一个关于C#/ .NET中法律指令重新排序的问题.
让我们从这个例子开始吧.我们在某个类中定义了此方法,其中_a,_b和_c是字段.
int _a;
int _b;
int _c;
void Foo()
{
_a = 1; // Write 1
_b = 1; // Write 2
_c = 1; // Write 3
}
Run Code Online (Sandbox Code Playgroud)
我们的呼叫环境看起来像这样.
//memory operations
ClassInstance.Foo();
//memory operations
Run Code Online (Sandbox Code Playgroud)
我想知道当这个方法调用内联对函数调用时,什么样的法律指令重新排序是可能的.更具体地说,我想知道在Foo()中重新排序内存操作是否/何时合法,内存操作在其外部(来自我们之前的示例,//内存操作).
此外,函数调用(无内联)在某种意义上是"生成内存障碍".同样,在函数调用之前或之后发生的内存操作不能与函数调用中的内存操作重新排序.
如果是这样,当它被编译器内联时,它仍会有这种"内存屏障"行为吗?
在多线程应用程序(ASP.NET MVC)上,我需要一个全局设置类,其中包含从Web.Config中获取的常量和值.
我想让这个类静态,作为单身......并锁定?
public static class Settings {
public static LoggerSettings Logger;
public static MailerSettings Mailer;
public class LoggerSettings {
public String Levels { get { return ConfigurationManager.AppSettings["Logger.Levels"]; } }
public const String Report = "team@xyz.com";
} // LoggerSettings
public class MailerSettings {
public String Contact { get { return ConfigurationManager.AppSettings["Mailer.Contact"]; } }
} // MailerSettings
}
Run Code Online (Sandbox Code Playgroud)
我想我应该实行双锁?没有?
我不确定这样做的最好方法.我能帮忙吗?
谢谢你,米格尔