我有一个简单的单例课程,
public class SimpleSingleton
{
public async Task<int> GetRefreshedValue()
{
/*
What goes here?
*/
return await GetRefreshedValueImplementation();
/*
What goes here?
*/
}
private async Task<int> GetRefreshedValueImplementation()
{
/*
Resource intensive and not thread safe
*/
}
}
由于这是一个单例,因此GetRefreshedValue
将被同时调用。我希望一次执行一个或零个任务GetRefreshedValueImplementation
。
这很简单,我可以使用SemaphoreSlim
。
private static SemaphoreSlim gate = new SemaphoreSlim(1);
...
await gate.WaitAsync();
try
{
return await GetRefreshedValueImplementation();
}
finally
{
gate.Release();
}
但是,我希望每个正在门口等待的任务都获得最近计算出的返回值。我不希望他们排队等候打电话。
编写该代码的最佳方法是什么?
答案 0 :(得分:2)
因此,操作本身非常简单。您只需要在操作开始时存储Task
并在操作完成后清除它,以便可以在任务运行时重新使用它。从那里开始,只是添加适当的同步,以便可以安全地在多个线程中使用(我认为是必需的,并且这并不需要全部通过单个同步上下文进行,如果需要,则可以删除锁定代码。)
public class Foo<T> //TODO come up with good name
{
private Func<Task<T>> factory;
private Task<T> currentInvocation;
private object key = new object();
public Foo(Func<Task<T>> factory)
{
this.factory = factory;
}
public Task<T> Value
{
get
{
lock (key)
{
if (currentInvocation == null)
{
currentInvocation = factory();
currentInvocation?.ContinueWith(_ =>
{
lock (key) { currentInvocation = null; }
});
}
return currentInvocation;
}
}
}
}
答案 1 :(得分:2)
public class SimpleSingleton
{
private static Task<int> executingTask;
private static object lockObject = new object();
public async Task<int> GetRefreshedValue()
{
lock (lockObject)
{
if (executingTask == null || executingTask.IsCompleted)
{
executingTask = GetRefreshedValueImplementation();
}
}
return await executingTask;
}
private async Task<int> GetRefreshedValueImplementation()
{
/*
Resource intensive and not thread safe
*/
}
}
答案 2 :(得分:0)
据我对您的情况的了解,您需要让电话获得相同正在进行的任务的结果,如果没有,则应创建一个新的电话。如果是这样,这将满足您的目的:
public class SimpleSingleton
{
private SimpleSingleton() { }
private static SimpleSingleton _instance;
public static SimpleSingleton Instance => _instance ?? (_instance = new SimpleSingleton());
public async Task<int> GetRefreshedValue()
{
return await GetRefreshedValueImplementation();
}
private volatile Task<int> _getRefreshedValueImplementationTask;
private Task<int> GetRefreshedValueImplementation()
{
if (_getRefreshedValueImplementationTask is null || _getRefreshedValueImplementationTask.IsCompleted)
{
return _getRefreshedValueImplementationTask = Task.Run(async () =>
{
/*
Resource intensive and not thread safe
*/
int r = new Random().Next(1000, 2001);
await Task.Delay(r);
return r;
});
}
return _getRefreshedValueImplementationTask;
}
}
答案 3 :(得分:-1)
类似的东西:
public class SimpleSingleton
{
private int _sequenceNo;
private int _lastvalue;
private object _lock = new object;
public async Task<int> GetRefreshedValue()
{
var currentSeq = _sequenceNo;
lock(_lock)
{
if (_sequenceNo == currentSeq)
{
_lastValue = await GetRefreshedValueImplementation();
_sequenceNo++;
}
}
return _lastValue;
}
private async Task<int> GetRefreshedValueImplementation()
{
/*
Resource intensive and not thread safe
*/
}
}