【发布时间】:2017-03-03 06:39:23
【问题描述】:
.NET 4.0 的 System.Lazy<T> 类通过枚举 LazyThreadSafetyMode 提供三种线程安全模式,我将其总结为:
- LazyThreadSafetyMode.None - 不是线程安全的。
- LazyThreadSafetyMode.ExecutionAndPublication - 只有一个并发线程会尝试创建底层值。成功创建后,所有等待的线程都将收到相同的值。如果在创建过程中发生未处理的异常,它将在每个等待线程上重新抛出,缓存并在随后每次尝试访问基础值时重新抛出。
- LazyThreadSafetyMode.PublicationOnly - 多个并发线程将尝试创建底层值,但第一个成功的线程将确定传递给所有线程的值。如果在创建过程中发生未处理的异常,它将不会被缓存,并且并发和后续尝试访问基础值将重新尝试创建并可能成功。
我想要一个延迟初始化的值,它遵循稍微不同的线程安全规则,即:
只有一个并发线程会尝试创建底层值。成功创建后,所有等待的线程都将收到相同的值。如果在创建过程中发生未处理的异常,它将在每个等待线程上重新抛出,但不会被缓存,后续访问底层值的尝试将重新尝试创建并可能成功。
因此,与 LazyThreadSafetyMode.ExecutionAndPublication 的主要区别在于,如果创建时的“第一次”失败,则可以稍后重新尝试。
是否存在提供这些语义的现有 (.NET 4.0) 类,还是我必须自己推出?如果我自己动手,是否有一种聪明的方法可以在实现中重用现有的 Lazy
注意对于一个用例,想象一下“创建”可能很昂贵并且容易出现间歇性错误,例如涉及从远程服务器获取大量数据。我不想进行多次并发尝试来获取数据,因为它们可能全部失败或全部成功。但是,如果它们失败了,我希望以后能够重试。
【问题讨论】:
-
“稍后重试”非常模糊。也许您可以使用重新创建 Lazy 实例的 Timer。
-
嗨@HansPassant。我并不是说 Lazy
本身应该稍后重试。我的意思是,如果用户多次调用 myLazy.value,那么如果第一次失败,那么在第二次调用时,它将再次尝试实例化底层值,而不是简单地重新抛出之前的异常。 -
“稍后”是什么时候?是“在主线程抛出异常并且所有等待线程都通过观察它被解除阻塞之后的任何时间”?它是否由任何呼叫者管理/正在观察您假设的
LazyWithSprinkles<T>?听起来有一个比您发布的问题稍大的问题,这表明与Lazy<T>类似的解决方案大不相同。 -
例如,也许你可以让
Lazy<Task<T>>在第一个请求时启动一个Task<T>重复发出请求直到它最终成功(或抛出一个它没有成功的错误知道如何从中恢复);然后,呼叫者可以等待Task<T>,只要他们可以证明是合理的。但是,它与您要查找的内容不完全相同,因为有时您会在值准备好之前超时 100 毫秒...但这实际上与稍后必须重试不同你的Lazy<T>-like 想法中抛出了异常? -
(另一种不同的方式是,如果所有调用者在看到第一个异常后都感到沮丧并且不想再试一次,这将在后台线程上浪费资源,可能会无限期地重试......再次,不知道用例的大局观,我很难说这是否可行,甚至有什么需要担心的)
标签: c# .net-4.0 thread-safety