在C#中从getter或setter调用异步方法最优雅的方法是什么?
这里有一些伪代码可以帮助解释自己.
async Task<IEnumerable> MyAsyncMethod()
{
return await DoSomethingAsync();
}
public IEnumerable MyList
{
get
{
//call MyAsyncMethod() here
}
}
Run Code Online (Sandbox Code Playgroud) 我想curl在我的C#控制台应用程序中进行以下调用:
curl -d "text=This is a block of text" \
http://api.repustate.com/v2/demokey/score.json
Run Code Online (Sandbox Code Playgroud)
我试着像这里发布的问题那样做,但我无法正确填写属性.
我还尝试将其转换为常规HTTP请求:
http://api.repustate.com/v2/demokey/score.json?text="This%20is%20a%20block%20of%20text"
Run Code Online (Sandbox Code Playgroud)
我可以将cURL调用转换为HTTP请求吗?如果是这样,怎么样?如果没有,我如何正确地从我的C#控制台应用程序进行上述cURL调用?
假设我有一个需要使用InitializeAsync()方法执行异步初始化的类.我想确保初始化只执行一次.如果另一个线程在初始化正在进行时调用此方法,它将"等待"直到第一个调用返回.
我正在考虑以下的实现(使用SemaphoreSlim).有更好/更简单的方法吗?
public class MyService : IMyService
{
private readonly SemaphoreSlim mSemaphore = new SemaphoreSlim(1, 1);
private bool mIsInitialized;
public async Task InitializeAsync()
{
if (!mIsInitialized)
{
await mSemaphore.WaitAsync();
if (!mIsInitialized)
{
await DoStuffOnlyOnceAsync();
mIsInitialized = true;
}
mSemaphore.Release();
}
}
private Task DoStuffOnlyOnceAsync()
{
return Task.Run(() =>
{
Thread.Sleep(10000);
});
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢!
编辑:
由于我正在使用DI并且将注入此服务,因此将其作为"懒惰"资源使用或使用异步工厂对我来说不起作用(尽管在其他用例中它可能很好).因此,异步初始化应该封装在类中,并对IMyService消费者透明.
将初始化代码包装在"虚拟" AsyncLazy<>对象中的想法将完成这项工作,尽管对我来说感觉有点不自然.
更新:@usr指出我错误地认为Lazy<T>默认的线程安全模式是LazyThreadSafetyMode.PublicationOnly......
我想懒洋洋地通过async工厂方法计算一个值(即它返回Task<T>)并在成功时缓存它.在例外情况下,我想让我可以使用它.我不然而,要堕入了异常缓存的行为是Lazy<T>在其默认模式(LazyThreadSafetyMode.ExecutionAndPublication)
异常缓存:使用工厂方法时,会缓存异常.也就是说,如果工厂方法在线程第一次尝试访问Lazy对象的Value属性时抛出异常,则每次后续尝试都会抛出相同的异常.这确保了对Value属性的每次调用都会产生相同的结果,并避免在不同的线程获得不同结果时可能出现的细微错误.懒惰代表一个实际的T,否则它将在某些早期点(通常在启动期间)初始化.在那个早期点失败通常是致命的.如果存在可恢复故障的可能性,我们建议您将重试逻辑构建到初始化例程(在本例中为工厂方法),就像您不使用延迟初始化一样.
Stephen Toub的AsyncLazy课程和写作似乎恰到好处:
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<Task<T>> taskFactory) :
base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap())
{ }
public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
}
Run Code Online (Sandbox Code Playgroud)
然而,这实际上与默认行为相同Lazy<T>- 如果出现问题,则不会重试.
我正在寻找一个Task<T>兼容的等价物Lazy<T>(Func<T>, LazyThreadSafetyMode.PublicationOnly),即它应该按照指定的行为: -
锁定的替代方法在某些情况下,您可能希望避免Lazy对象的默认锁定行为的开销.在极少数情况下,可能存在死锁的可能性.在这种情况下,您可以使用Lazy(LazyThreadSafetyMode)或Lazy(Func,LazyThreadSafetyMode)构造函数,并指定LazyThreadSafetyMode.PublicationOnly.如果线程同时调用Value属性,这使Lazy对象能够在多个线程中的每个线程上创建一个延迟初始化对象的副本.Lazy对象确保所有线程使用延迟初始化对象的相同实例并丢弃未使用的实例.因此,降低锁定开销的成本是您的程序有时可能会创建并丢弃昂贵对象的额外副本.在大多数情况下,这不太可能.Lazy(LazyThreadSafetyMode)和Lazy(Func,LazyThreadSafetyMode)构造函数的示例演示了此行为.
重要
指定PublicationOnly时,即使指定了工厂方法,也不会缓存异常.
是否有任何FCL Nito.AsyncEx或类似的结构可能适合这里?如果没有这个,任何人都可以看到一种优雅的方式来控制"尝试进行中"位(我很好,每个调用者以与Lazy<T>(... 相同的方式进行自己的尝试(LazyThreadSafetyMode.PublicationOnly))并且仍然具有该缓存管理封装整齐?