lambda 中的初始化惰性字段未在多个线程之间重用
Initialized Lazy field in lambda not being reused among multiple threads
我有一个并行生成多个线程 (lambda) 的方法,在执行期间它访问 class 中定义的 Lazy 字段 属性,其中调用此方法的方式如下:
class A {
private Lazy<FabricClient> _fabricClient => new Lazy<FabricClient>(() => GetDefaultFabricClient());
private FabricClient FabricClient => _fabricClient.Value;
internal A()
{
Console.WriteLine(FabricClient.ToString());
}
private void tempMethod()
{
List<String> listOfStrings = GetStrings();
RunThreads(listOfStrings.Select<string, ThreadStart>(tempString => () =>
{
var x = FabricClient.GetServiceList();
}).ToArray());
}
private FabricClient GetDefaultFabricClient()
{
// Environment is inherited property, I cannot edit it
// And it's defined like
//
// public Environment Environment
// { get { return _context.Environment; }}
//
if (Environment.IsPublicEnvironment)
{
return new FabricClient(Credentials, Endpoint);
}
return new FabricClient();
}
}
是否有可能确保所有线程都将访问相同的 属性、相同的对象(因为目前每个线程都在初始化自己的 FabricClient Lazy 对象,而不是重复使用前一个被初始化的对象,可能不会使其成为静态对象) ?
在 tempMethod 执行之前也填充了惰性 FabricClient 属性,但它没有在 RunThreads 方法中重用。
这是错误的,Lazy默认是线程安全的
您可能需要在使用惰性对象的地方使用某种锁,因为所有这些线程都可能会重新初始化惰性对象,因为它们可能都在它完全创建之前调用它。
类似于:
lock(syncObject)
{
var client = FabricClient;
}
var x = FabricClient.GetServiceList();
其中同步对象只是一个标准的新 clr 对象
object syncObject = new object();
您已将 _fabricClient
属性 定义为:
private Lazy<FabricClient> _fabricClient => new Lazy<FabricClient>(() => {
return new FabricClient();
});
这(由于使用 =>
)表示 "every time _fabricClient
is accessed, create a new Lazy"。这与你想要的相反。
你想要一个普通字段,而不是 属性(你最好将其设置为只读):
private readonly Lazy<FabricClient> _fabricClient = new Lazy<FabricClient>(() => {
return new FabricClient();
});
这将创建一个 Lazy 实例,并在构造 A
实例时将其存储在 _fabricClient
字段中。每次访问_fabricClient
(通过FabricClient
属性),都会得到相同的Lazy实例。
为了更清楚一点,您的 _fabricClient
属性 与:
private Lazy<FabricClient> _fabricClient
{
get
{
return new Lazy<FabricClient>(() => {
return new FabricClient();
});
}
}
你可以在这里添加一些日志记录,看到每次访问这个属性时都会执行getter:
private Lazy<FabricClient> _fabricClient
{
get
{
Console.WriteLine("Constucting a new Lazy");
return new Lazy<FabricClient>(() => {
return new FabricClient();
});
}
}
我有一个并行生成多个线程 (lambda) 的方法,在执行期间它访问 class 中定义的 Lazy 字段 属性,其中调用此方法的方式如下:
class A {
private Lazy<FabricClient> _fabricClient => new Lazy<FabricClient>(() => GetDefaultFabricClient());
private FabricClient FabricClient => _fabricClient.Value;
internal A()
{
Console.WriteLine(FabricClient.ToString());
}
private void tempMethod()
{
List<String> listOfStrings = GetStrings();
RunThreads(listOfStrings.Select<string, ThreadStart>(tempString => () =>
{
var x = FabricClient.GetServiceList();
}).ToArray());
}
private FabricClient GetDefaultFabricClient()
{
// Environment is inherited property, I cannot edit it
// And it's defined like
//
// public Environment Environment
// { get { return _context.Environment; }}
//
if (Environment.IsPublicEnvironment)
{
return new FabricClient(Credentials, Endpoint);
}
return new FabricClient();
}
}
是否有可能确保所有线程都将访问相同的 属性、相同的对象(因为目前每个线程都在初始化自己的 FabricClient Lazy 对象,而不是重复使用前一个被初始化的对象,可能不会使其成为静态对象) ?
在 tempMethod 执行之前也填充了惰性 FabricClient 属性,但它没有在 RunThreads 方法中重用。
这是错误的,Lazy默认是线程安全的
您可能需要在使用惰性对象的地方使用某种锁,因为所有这些线程都可能会重新初始化惰性对象,因为它们可能都在它完全创建之前调用它。
类似于:
lock(syncObject)
{
var client = FabricClient;
}
var x = FabricClient.GetServiceList();
其中同步对象只是一个标准的新 clr 对象
object syncObject = new object();
您已将 _fabricClient
属性 定义为:
private Lazy<FabricClient> _fabricClient => new Lazy<FabricClient>(() => {
return new FabricClient();
});
这(由于使用 =>
)表示 "every time _fabricClient
is accessed, create a new Lazy"。这与你想要的相反。
你想要一个普通字段,而不是 属性(你最好将其设置为只读):
private readonly Lazy<FabricClient> _fabricClient = new Lazy<FabricClient>(() => {
return new FabricClient();
});
这将创建一个 Lazy 实例,并在构造 A
实例时将其存储在 _fabricClient
字段中。每次访问_fabricClient
(通过FabricClient
属性),都会得到相同的Lazy实例。
为了更清楚一点,您的 _fabricClient
属性 与:
private Lazy<FabricClient> _fabricClient
{
get
{
return new Lazy<FabricClient>(() => {
return new FabricClient();
});
}
}
你可以在这里添加一些日志记录,看到每次访问这个属性时都会执行getter:
private Lazy<FabricClient> _fabricClient
{
get
{
Console.WriteLine("Constucting a new Lazy");
return new Lazy<FabricClient>(() => {
return new FabricClient();
});
}
}