【问题标题】:How I can GetAsync in a thread safe manner (memoryCache) c#我如何以线程安全的方式获取异步(memoryCache)c#
【发布时间】:2020-11-07 12:20:39
【问题描述】:

我正在为 memoryCache 寻找线程安全的“GetAsync”版本。

我知道有“GetOrCreateAsync”,但在提交一些数据之前,我需要检查它是否存在于缓存中。

我一直在谷歌上搜索,但我没有找到一个 ThreadSafe “GetAsync” 方法,即使在有赛车条件的情况下我也可以安全使用。

是否有“GetOrCreateAsync”版本但只是 GET?

有什么建议吗?

更新

最后我使用了 GetOrAddAsync,如下所示

  private readonly SemaphoreSlim locker = new SemaphoreSlim(1, 1);
    public async Task<T> GetOrAddAsync<T>(object key, Func<ICacheEntry, Task<T>> create)
    {
        await locker.WaitAsync();
        try
        {
            return await cache.GetOrCreateAsync(key, create);
        }
        finally
        {
            locker.Release();
        }
    }

我是这样测试的

    var tasks = new List<Task>();
        var counter = 0;

        for (int i = 0; i < 10; i++)
        {
            var index = i;
            tasks.Add(Task.Run(async () =>
            {
                var x = await cache.GetOrAddAsync("test", entry => Task.FromResult(Interlocked.Increment(ref counter)));
                output.WriteLine($"Interaction {index} got {x}");
            }));
        }
        await Task.WhenAll(tasks);
        output.WriteLine("Counter {0}", counter);
        Assert.True(counter == 1);
    }

这个测试有什么好处并证明可以处理竞争条件吗?

【问题讨论】:

  • 内存缓存没有理由拥有GetAsync 方法,因为在get 上没有什么可以做的异步操作。它是一个内存缓存,因此没有 IO 可以从外部源(例如数据库或文件)获取缓存值。
  • @Evk 感谢您的解释,我想我正在掌握所有这些缓存内容及其工作原理。如果您喜欢 Get 找不到该项目,因为 add 仍在执行此操作,是否存在竞争条件?我有一个只应该执行一次的方法,我添加了一个“键”来缓存,例如“ref123”,但如果再次调用,我只需要搜索“ref123”,如果找到不执行方法。希望是有道理的
  • 但这不正是GetOrCreate(或GetOrCreateAsync,如果获取值的方法是异步的)所做的吗?
  • 让我们假设我只想缓存一个字符串值,例如“ref123”,过期时间为 3 分钟 GetOrCreateAsync 等。将始终返回我一个值,因为如果不存在它会创建一个值,我需要什么在我的场景中简单来说,如果一个值不存在 GetAsync 将返回 null
  • @aepot 嗨,你有使用它的样本吗

标签: c# caching xamarin.forms memorycache


【解决方案1】:

SemaphoreSlim 电动锁。

例如,您有一个方法必须一次只能由单个线程访问,例如:

public async Task<MyData> GetDataAsync(string request); // not thread-safe!

数据请求数组,例如

string[] requests;

并且您希望并行处理这些请求,但使用某些执行不安全调用的方法以线程安全的方式进行外部调用。

private async Task MakeRequestAsync(string request, SemaphoreSlim semaphore)
{
    await PrepareAsync();
    MyData data = null;

    await semaphore.WaitAsync(); // begin sync code
    try
    {
        data = await GetDataAsync(request);
    }
    finally
    {
        semaphore.Release();  // end sync code
    }

    if (data != null)
        await ProcessResultAsync(data);
}
List<Task> tasks = new List<Task>();
using (SemaphoreSlim semaphore = new SemaphoreSlim(1)) // 1 = concurrency degree
{
    foreach (string request in requests)
        tasks.Add(MakeRequestAsync(request, semaphore));
    await Task.WhenAll(tasks);
}

【讨论】:

  • @aeapot 感谢您的代码。消化一下,试着放在一起测试,证明只有一个方法只执行一次
  • 你成功了吗?您可以在这里分享解决方案:)。
  • @JackHua-MSFT 我还没有成功,因为我需要证明它有效并且诚实地努力进行测试以证明这一点
  • 好的,您也可以在这里分享您在测试时遇到的困难,我们可以为您提供帮助。
  • @aepot 感谢您的输入,正如您在更新部分中看到的那样,我已经适应了。你如何测试它并证明它可以在竞争条件下工作?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 2014-10-16
  • 2021-08-19
  • 2011-04-06
  • 2021-04-27
相关资源
最近更新 更多