我的情况是我有一个特殊工厂创建的对象树。这有点类似于DI容器,但不完全相同。
对象的创建总是通过构造函数发生,并且对象是不可变的。
在给定的执行中可能不需要对象树的某些部分,应该懒惰地创建。因此构造函数参数应该只是按需创建的工厂。这似乎是Lazy
的工作。
但是,对象创建可能需要访问慢资源,因此始终是异步的。 (对象工厂的创建函数返回Task
。)这意味着Lazy
的创建函数需要是异步的,因此注入的类型需要为{{1} }。
但我宁愿没有双层包装。我想知道是否有可能迫使Lazy<Task<Foo>>
变得懒惰,即创建一个保证在等待之前不会执行的Task
。据我了解,Task
或Task.Run
可能随时开始执行(例如,如果来自池的线程空闲),即使没有任何东西在等待它。
Task.Factory.StartNew
答案 0 :(得分:20)
我不确定您为什么要避免使用Lazy<Task<>>,
,但如果只是为了让API更易于使用,因为这是一个属性,您可以使用支持字段来执行此操作:
public class SomePart
{
private readonly Lazy<Task<SlowPart>> _lazyPart;
public SomePart(OtherPart eagerPart, Func<Task<SlowPart>> lazyPartFactory)
{
_lazyPart = new Lazy<Task<SlowPart>>(lazyPartFactory);
EagerPart = eagerPart;
}
OtherPart EagerPart { get; }
Task<SlowPart> LazyPart => _lazyPart.Value;
}
这样,用法就好像它只是一个任务,但初始化是懒惰的,只有在需要时才会产生。
答案 1 :(得分:2)
@最大&#39;答案很好,但我想添加一个建立在Stephen Toub&#39;之上的版本。评论中提到的文章:
public class SomePart: Lazy<Task<SlowPart>>
{
public SomePart(OtherPart eagerPart, Func<Task<SlowPart>> lazyPartFactory)
: base(() => Task.Run(lazyPartFactory))
{
EagerPart = eagerPart;
}
public OtherPart EagerPart { get; }
public TaskAwaiter<SlowPart> GetAwaiter() => Value.GetAwaiter();
}
SomePart明确继承自Lazy<Task<>>
,因此很清楚它是 lazy 和 asyncronous 。
调用基础构造函数将lazyPartFactory
包装到Task.Run
以避免长块,如果该工厂在真正的异步部分之前需要一些cpu繁重的工作。如果不是您的情况,只需将其更改为base(lazyPartFactory)
可以通过TaskAwaiter访问SlowPart。所以SomePart&#39;公共接口是:
var eagerValue = somePart.EagerPart;
var slowValue = await somePart;
答案 2 :(得分:0)
使用Task
的构造函数使任务变得懒惰a.k.a在你说它运行之前没有运行,所以你可以这样做:
public class TestLazyTask
{
private Task<int> lazyPart;
public TestLazyTask(Task<int> lazyPart)
{
this.lazyPart = lazyPart;
}
public Task<int> LazyPart
{
get
{
// You have to start it manually at some point, this is the naive way to do it
this.lazyPart.Start();
return this.lazyPart;
}
}
}
public static async void Test()
{
Trace.TraceInformation("Creating task");
var lazyTask = new Task<int>(() =>
{
Trace.TraceInformation("Task run");
return 0;
});
var taskWrapper = new TestLazyTask(lazyTask);
Trace.TraceInformation("Calling await on task");
await taskWrapper.LazyPart;
}
结果:
SandBox.exe Information: 0 : Creating task
SandBox.exe Information: 0 : Calling await on task
SandBox.exe Information: 0 : Task run
然而我强烈建议您使用Rx.NET和IObservable
,因为在您的情况下,您可以减少处理不太天真的案件以便在右侧开始任务的麻烦时刻。
此外,它使我的代码中的代码更清晰
public class TestLazyObservable
{
public TestLazyObservable(IObservable<int> lazyPart)
{
this.LazyPart = lazyPart;
}
public IObservable<int> LazyPart { get; }
}
public static async void TestObservable()
{
Trace.TraceInformation("Creating observable");
// From async to demonstrate the Task compatibility of observables
var lazyTask = Observable.FromAsync(() => Task.Run(() =>
{
Trace.TraceInformation("Observable run");
return 0;
}));
var taskWrapper = new TestLazyObservable(lazyTask);
Trace.TraceInformation("Calling await on observable");
await taskWrapper.LazyPart;
}
结果:
SandBox.exe Information: 0 : Creating observable
SandBox.exe Information: 0 : Calling await on observable
SandBox.exe Information: 0 : Observable run
更清楚:Observable
这里处理何时启动任务,默认情况下它是Lazy并且每次订阅时都会运行任务(这里订阅由awaiter使用,允许使用await
关键字)。
如果需要,您可以每隔一分钟(或任何时间)运行一次任务,并在所有订阅者中发布结果以保存性能,例如在真实世界的应用程序中,所有这些以及更多由观察者处理。
答案 3 :(得分:-1)
声明:
private Lazy<Task<ServerResult>> _lazyServerResult;`
ctor()
{
_lazyServerResult = new Lazy<Task<ServerResult>>(async () => await
GetServerResultAsync())
}
用法:
ServerResult result = await __lazyServerResult.Value;