给出:服务器,可以发出服务请求。服务一次只能执行一个请求。 我想将正在运行的任务缓存到服务中,例如如果服务器执行对第一客户的请求并接收到第二客户的请求,则服务器将已创建的任务返回给第二客户,而不是等待第一任务完成并启动第二个请求。
第一个版本带有锁:
public class Lock
{
private readonly object _lock = new object();
private Task<long> _task;
public Task<long> GetServiceResultAsync()
{
async Task<long> GetTaskAsync()
{
// to execute lock scope very fast
await Task.Yield();
return await GetServiceResultInternalAsync();
}
lock (_lock)
{
if (_task == null ||
_task.IsCompleted)
{
_task = GetTaskAsync();
_task.ContinueWith(task =>
{
lock (_lock)
{
_task = null;
}
});
}
return _task;
}
}
private async Task<long> GetServiceResultInternalAsync()
{
await Task.Delay(1000);
return DateTime.Now.Ticks;
}
}
但是对我来说看起来并不好。所以我用互锁将其重写:
public class Interlock
{
private Lazy<Task<long>> _task;
public Task<long> GetServiceResultAsync()
{
return TaskCacheHelper.GetOrCreateTask(ref _task, GetServiceResultInternalAsync, NullifyStartUpgradeTask);
}
private void NullifyStartUpgradeTask(Lazy<Task<long>> lazy)
{
Interlocked.CompareExchange(ref _task, null, lazy);
}
private async Task<long> GetServiceResultInternalAsync()
{
await Task.Delay(1000);
return DateTime.Now.Ticks;
}
}
public static class TaskCacheHelper
{
public static Task<T> GetOrCreateTask<T>(ref Lazy<Task<T>> field, Func<Task<T>> getTask, Action<Lazy<Task<T>>> nullifyField)
{
var oldField = field;
if (oldField != null && !oldField.Value.IsCompleted)
{
return oldField.Value;
}
// use Lazy to prevent task from executing 2 times
var newValue = new Lazy<Task<T>>(getTask);
var originalFieldValue = Interlocked.CompareExchange(ref field, newValue, oldField);
if (originalFieldValue == oldField)
{
// external delegate used here because ref parameter can't be used inside lambda
newValue.Value.ContinueWith(task => nullifyField(newValue));
return newValue.Value;
}
return originalFieldValue.Value;
}
}
第二个版本看起来更好,但是我对Nullify方法感到困惑:这是常见的逻辑,但每次都应编写。
所以,问题是:
用法:
static async Task Main(string[] args)
{
var pr = new Interlock();
var t1 = pr.GetServiceResultAsync();
var t2 = pr.GetServiceResultAsync();
await Task.WhenAll(t1, t2);
Debug.Assert(t1.Result == t2.Result);
var t3 = await pr.GetServiceResultAsync();
var t4 = await pr.GetServiceResultAsync();
Debug.Assert(t3 != t4);
}