c# 使用Memory实现Redis入队出队功能

使用net6.0的memory实现Redis的入队出队功能

nuget包:

Microsoft.Extensions.Caching.Memory
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
    
    public interface IMemoryCacheListService
    {
        /// <summary>
        /// 在列表的尾部(右侧)添加一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        Task<long> RPushAsync<T>(string key, T value, TimeSpan? expiry = null);

        /// <summary>
        /// 在列表的头部(左侧)添加一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        Task<long> LPushAsync<T>(string key, T value, TimeSpan? expiry = null);

        /// <summary>
        /// 移除并返回列表头部(左侧)的第一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<T> LPopAsync<T>(string key);

        /// <summary>
        /// 移除并返回列表尾部(右侧)的最后一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<T> RPopAsync<T>(string key);

        /// <summary>
        /// 返回列表中指定范围内的元素。范围由起始索引(start)和结束索引(stop)指定,支持负数索引(表示从尾部开始计算)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        Task<List<T>> LRangeAsync<T>(string key, int start, int stop);

        /// <summary>
        /// 返回列表的长度
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<long> LLenAsync<T>(string key);

        /// <summary>
        /// 通过索引获取列表中的元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        Task<T> LIndexAsync<T>(string key, int index);

        /// <summary>
        /// 为列表设置过期时间
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        Task<bool> ExpireAsync<T>(string key, TimeSpan expiry);

        /// <summary>
        /// 修剪列表,只保留指定范围内的元素,其他元素将被删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        Task<bool> LTrimAsync<T>(string key, int start, int stop);

        /// <summary>
        /// 移除列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<bool> RemoveAsync<T>(string key);

        /// <summary>
        /// 获取列表中的所有元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        IAsyncEnumerable<T> GetItemsAsync<T>(string key);
    }

    public class MemoryCacheListService : IMemoryCacheListService, IDisposable
    {
        private readonly IMemoryCache _memoryCache;
        private readonly IOptions<CustomMemoryCacheOptions> _options;
        private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphores = new();

        public MemoryCacheListService(
            IMemoryCache memoryCache,
            IOptions<CustomMemoryCacheOptions> options = null)
        {
            _memoryCache = memoryCache;
            _options = options;
        }

        /// <summary>
        /// 在列表的尾部(右侧)添加一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public async Task<long> RPushAsync<T>(string key, T value, TimeSpan? expiry = null)
        {
            var cacheKey = GetCacheKey(key);
            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));

            await semaphore.WaitAsync();

            try
            {
                if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null)
                {
                    list = new List<T>();
                }

                list.Add(value);
                SetListWithExpiry(cacheKey, list, expiry);
                return list.Count;
            }
            finally
            {
                semaphore.Release();
            }
        }

        /// <summary>
        /// 在列表的头部(左侧)添加一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public async Task<long> LPushAsync<T>(string key, T value, TimeSpan? expiry = null)
        {
            var cacheKey = GetCacheKey(key);
            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));

            await semaphore.WaitAsync();

            try
            {
                if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null)
                {
                    list = new List<T>();
                }

                list.Insert(0, value);
                SetListWithExpiry(cacheKey, list, expiry);
                return list.Count;
            }
            finally
            {
                semaphore.Release();
            }
        }

        /// <summary>
        /// 移除并返回列表头部(左侧)的第一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> LPopAsync<T>(string key)
        {
            var cacheKey = GetCacheKey(key);
            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));

            await semaphore.WaitAsync();

            try
            {
                if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null || list.Count == 0)
                {
                    return default(T);
                }

                var value = list[0];
                list.RemoveAt(0);
                if (list.Count > 0)
                {
                    SetListWithExpiry(cacheKey, list);
                }
                else
                {
                    _memoryCache.Remove(cacheKey);
                }

                return value;
            }
            finally
            {
                semaphore.Release();
            }
        }

        /// <summary>
        /// 移除并返回列表尾部(右侧)的最后一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> RPopAsync<T>(string key)
        {
            var cacheKey = GetCacheKey(key);
            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));

            await semaphore.WaitAsync();

            try
            {
                if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null || list.Count == 0)
                {
                    return default(T);
                }

                // 修复:正确获取最后一个元素
                var value = list[list.Count - 1]; // 正确的索引访问
                list.RemoveAt(list.Count - 1);
                if (list.Count > 0)
                {
                    SetListWithExpiry(cacheKey, list);
                }
                else
                {
                    _memoryCache.Remove(cacheKey);
                }
                return value;
            }
            finally
            {
                semaphore.Release();
            }
        }

        /// <summary>
        /// 返回列表中指定范围内的元素。范围由起始索引(start)和结束索引(stop)指定,支持负数索引(表示从尾部开始计算)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public async Task<List<T>> LRangeAsync<T>(string key, int start, int stop)
        {
            var cacheKey = GetCacheKey(key);

            if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null)
            {
                return new List<T>();
            }

            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));
            await semaphore.WaitAsync();

            try
            {
                var normalizedStart = NormalizeIndex(start, list.Count);
                var normalizedStop = NormalizeIndex(stop, list.Count);

                normalizedStart = Math.Max(0, normalizedStart);
                normalizedStop = Math.Min(list.Count - 1, normalizedStop);

                if (normalizedStart > normalizedStop)
                    return new List<T>();

                return list.GetRange(normalizedStart, normalizedStop - normalizedStart + 1);
            }
            finally
            {
                semaphore.Release();
            }
        }

        /// <summary>
        /// 返回列表的长度
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<long> LLenAsync<T>(string key)
        {
            var cacheKey = GetCacheKey(key);

            if (_memoryCache.TryGetValue(cacheKey, out List<T> list) && list != null)
            {
                return list.Count;
            }

            return 0;
        }

        /// <summary>
        /// 通过索引获取列表中的元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public async Task<T> LIndexAsync<T>(string key, int index)
        {
            var cacheKey = GetCacheKey(key);

            if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null || list.Count == 0)
            {
                return default(T);
            }

            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));
            await semaphore.WaitAsync();

            try
            {
                var normalizedIndex = NormalizeIndex(index, list.Count);
                if (normalizedIndex < 0 || normalizedIndex >= list.Count)
                    return default(T);

                return list[normalizedIndex];
            }
            finally
            {
                semaphore.Release();
            }
        }

        /// <summary>
        /// 为列表设置过期时间
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public async Task<bool> ExpireAsync<T>(string key, TimeSpan expiry)
        {
            var cacheKey = GetCacheKey(key);
            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));

            await semaphore.WaitAsync();

            try
            {
                if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null)
                {
                    return false;
                }

                SetListWithExpiry(cacheKey, list, expiry);
                return true;
            }
            finally
            {
                semaphore.Release();
            }
        }

        /// <summary>
        /// 修剪列表,只保留指定范围内的元素,其他元素将被删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public async Task<bool> LTrimAsync<T>(string key, int start, int stop)
        {
            var cacheKey = GetCacheKey(key);
            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));

            await semaphore.WaitAsync();

            try
            {
                if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null)
                {
                    return false;
                }

                var normalizedStart = NormalizeIndex(start, list.Count);
                var normalizedStop = NormalizeIndex(stop, list.Count);

                normalizedStart = Math.Max(0, normalizedStart);
                normalizedStop = Math.Min(list.Count - 1, normalizedStop);

                if (normalizedStart > normalizedStop)
                {
                    list.Clear();
                }
                else
                {
                    var newList = list.GetRange(normalizedStart, normalizedStop - normalizedStart + 1);
                    list.Clear();
                    list.AddRange(newList);
                }

                if (list.Count > 0)
                {
                    SetListWithExpiry(cacheKey, list);
                }
                else
                {
                    _memoryCache.Remove(cacheKey);
                }

                return true;
            }
            finally
            {
                semaphore.Release();
            }
        }

        /// <summary>
        /// 移除列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<bool> RemoveAsync<T>(string key)
        {
            var cacheKey = GetCacheKey(key);
            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));

            await semaphore.WaitAsync();

            try
            {
                _memoryCache.Remove(cacheKey);
                return true;
            }
            finally
            {
                semaphore.Release();
                _semaphores.TryRemove(cacheKey, out _);
            }
        }

        /// <summary>
        /// 获取列表中的所有元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async IAsyncEnumerable<T> GetItemsAsync<T>(string key)
        {
            var cacheKey = GetCacheKey(key);

            if (!_memoryCache.TryGetValue(cacheKey, out List<T> list) || list == null)
            {
                yield break;
            }

            var semaphore = _semaphores.GetOrAdd(cacheKey, _ => new SemaphoreSlim(1, 1));
            await semaphore.WaitAsync();

            try
            {
                foreach (var item in list)
                {
                    yield return item;
                }
            }
            finally
            {
                semaphore.Release();
            }
        }

        private string GetCacheKey(string key) => $"list_{key}";

        private void SetListWithExpiry<T>(string cacheKey, List<T> list, TimeSpan? expiry = null)
        {
            var cacheOptions = new MemoryCacheEntryOptions();

            if (expiry.HasValue)
            {
                cacheOptions.AbsoluteExpirationRelativeToNow = expiry;
            }
            else
            {
                cacheOptions.SlidingExpiration = _options?.Value.DefaultSlidingExpiration;
                cacheOptions.AbsoluteExpirationRelativeToNow = _options?.Value.DefaultAbsoluteExpiration;
            }

            _memoryCache.Set(cacheKey, list, cacheOptions);
        }

        private int NormalizeIndex(int index, int count)
        {
            return index < 0 ? count + index : index;
        }

        public void Dispose()
        {
            foreach (var semaphore in _semaphores.Values)
            {
                semaphore.Dispose();
            }
            _semaphores.Clear();
        }
    }

    public class CustomMemoryCacheOptions
    {
        /// <summary>
        /// 相对过期时间(滑动过期时间)
        /// </summary>
        public TimeSpan? DefaultSlidingExpiration { get; set; } = TimeSpan.FromMinutes(30);

        /// <summary>
        /// 绝对过期时间
        /// </summary>
        public TimeSpan? DefaultAbsoluteExpiration { get; set; } = TimeSpan.FromHours(1);
    }

Sample:

var key = "key";

var services = new ServiceCollection();// 创建ServiceCollection
// 注册缓存服务
services.Configure<CustomMemoryCacheOptions>(options =>
{
    options.DefaultSlidingExpiration = TimeSpan.FromMinutes(30);
    options.DefaultAbsoluteExpiration = TimeSpan.FromHours(2);
});
services.AddTransient<IMemoryCacheListService, MemoryCacheListService>();
services.AddMemoryCache();
var serviceProvider = services.BuildServiceProvider();


var stackService = serviceProvider.GetRequiredService<IMemoryCacheListService>();

#region Queue 先进先出

Console.WriteLine($"Queue 先进先出");

await stackService.RPushAsync(key, "item1");
await stackService.RPushAsync(key, "item2");
await stackService.RPushAsync(key, "item3");

while (true)
{
    string value = await stackService.LPopAsync<string>(key);
    if (value == null)
        break;
    Console.WriteLine($"Time:{DateTime.Now:yyyy-MM-dd HH:mm:ss} Message: {value}");
    await Task.Delay(1000);
}

#endregion

#region Stack 先进后出

Console.WriteLine($"Stack 先进后出");

await stackService.RPushAsync(key, "item1");
await stackService.RPushAsync(key, "item2");
await stackService.RPushAsync(key, "item3");

while (true)
{
    string value = await stackService.RPopAsync<string>(key);
    if (value == null)
        break;
    Console.WriteLine($"Time:{DateTime.Now:yyyy-MM-dd HH:mm:ss} Message: {value}");
    await Task.Delay(1000);
}

#endregion

Console.ReadKey();

output:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值