【发布时间】:2017-07-26 17:42:57
【问题描述】:
我的代码中存在一个问题,即调用延迟初始化程序的频率超出了我的预期。从文档中,我预计使用 LazyThreadSafetyMode.ExecutionAndPublication 将确保我的初始化函数只被调用一次,例如,如果在定义后访问 numbers.Value:
numbers = new Lazy<IEnumerable<int>>(
() => GetNumbers(),
LazyThreadSafetyMode.ExecutionAndPublication
);
但是,我发现如果初始化函数产生结果,初始化函数会被多次调用。我认为这与延迟执行有关,但我只有模糊的感觉。
问题:
在下面的代码中,为什么各个初始化函数的执行次数不同?
void Main()
{
var foo = new foo();
var tasks = new List<Task>();
for (int i = 0; i < 10; ++i) tasks.Add(Task.Run(() => {foreach (var number in foo.Numbers) Debug.WriteLine(number);}));
Task.WaitAll(tasks.ToArray());
tasks.Clear();
for (int i = 0; i < 10; ++i) tasks.Add(Task.Run(() => {foreach (var letter in foo.Letters) Debug.WriteLine(letter);}));
Task.WaitAll(tasks.ToArray());
}
public class foo
{
public IEnumerable<int> Numbers => numbers.Value;
public IEnumerable<char> Letters => letters.Value;
readonly Lazy<IEnumerable<int>> numbers;
readonly Lazy<IEnumerable<char>> letters;
public foo()
{
numbers = new Lazy<IEnumerable<int>>(
() => GetNumbers(),
LazyThreadSafetyMode.ExecutionAndPublication
);
letters = new Lazy<IEnumerable<char>>(
() => GetLetters().ToList(), //ToList enumerates all yielded letters, creating the expected call once behavior
LazyThreadSafetyMode.ExecutionAndPublication
);
}
protected IEnumerable<char> GetLetters()
{
Debug.WriteLine($"{nameof(GetLetters)} Called");
yield return 'a';
yield return 'b';
yield return 'c';
yield break;
}
protected IEnumerable<int> GetNumbers()
{
Debug.WriteLine($"{nameof(GetNumbers)} Called");
yield return 1;
yield return 2;
yield return 3;
yield break;
}
}
【问题讨论】:
-
您需要了解
yield的工作原理。基本上你的方法变成了状态机。每次你迭代它时,你的代码都会被调用。这实际上与Lazy<T>无关,正如@dcg 指出的那样,使用yield已经很懒惰,因此无需将其包装在Lazy<T>对象中。如果您想将方法的结果缓存在内存中,您可以使用ToList,然后Lazy<T>可用于延迟ToList调用迭代该方法并将结果放入内存。
标签: c# ienumerable lazy-initialization