.Net Core Async 关键部分(如果在同一实体上工作)

.Net Core Async critical section if working on same entity

我需要确保通过 web API 访问的方法不能同时被多个调用访问,如果它在具有相同 id 的同一个对象上工作

我理解 SemaphoreSlim 的用法,但是简单地实现它会锁定所有的关键部分。但我只需要锁定该部分,前提是它适用于同一实体而不适用于 2 个不同的

这是我的场景,一个用户开始工作,创建实体并准备修改,然后一个或多个用户可以操作这个实体,但是这个操作的一部分必须在关键部分否则会导致数据不一致,当工作完成后,实体将从工作状态中移除并移动到存档中,并且只能以只读方式访问

包含该函数的 class 在应用程序启动时作为瞬态注入

 services.AddTransient<IWorkerService>(f => new WorkerService(connectionString));
public async Task<int> DoStuff(int entityId)
{
  //Not Critical Stuff

  //Critical Stuff

  ReadObjectFromRedis();
  ManipulateObject();
  UpdateSqlDatabase();
  SaveObjectToRedis();

 //Not Critical Stuff
}

我怎样才能做到这一点?

试试这个,我不确定这些对象是否可用。net-core

class Controller
{
    private static ConcurrentDictionary<int, SemaphoreSlim> semaphores = new ConcurrentDictionary<int, SemaphoreSlim>();

    public async Task<int> DoStuff(int entityId)
    {
        SemaphoreSlim sem = semaphores.GetOrAdd(entityId, ent => new SemaphoreSlim(0, 1));
        await sem.WaitAsync();
        try
        {
            //do real stuff
        }
        finally
        {
            sem.Release();
        }
    }
}

这不是一个容易解决的问题。我对缓存有类似的问题:我希望当缓存过期时只调用一次来重新填充它。令牌的非常常见的方法,例如你必须不时更新。

普通使用信号量的一个问题是,在您退出后,所有等待的线程都会进入并再次调用,这就是为什么您需要双重检查锁定来修复它。如果您可以为您提供一些本地状态,我不确定(我想您有,因为您有理由只进行一次调用并且最有可能具有状态),但这是我为令牌缓存解决它的方法:

    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);

    public async Task<string> GetOrCreateAsync(Func<Task<TokenResponse>> getToken)
    {
        string token = Get();
        if (token == null)
        {
            await _semaphore.WaitAsync();
            try
            {
                token = Get();
                if (token == null)
                {
                    var data = await getToken();
                    Set(data);
                    token = data.AccessToken;
                }
            }
            finally
            {
                _semaphore.Release();
            }
        }

        return token;
    }

现在我真的不知道它是否防弹。如果它是普通的双重检查锁定(不是异步),那么它就不是,尽管解释为什么真的很难,并且要解释处理器如何在幕后执行多线程以及它们如何重新排序指令。

但是在缓存的情况下,如果有一个千载难逢的双重调用并不是什么大问题。

我还没有找到更好的方法来做到这一点,这是由 e.g. 提供的示例。 Scott Hanselman 并在 Stack Overflow 上也发现了一些地方。

为此,使用信号量是多余的。命名的互斥量就足够了。

class Foo
{
    public void Bar(int id)
    {
        using var mutex = new Mutex(false, id.ToString(), out var createdNew);

        if (createdNew)
        {
            // Business logic here.
        }
    }
}