.Net中的键锁 [英] Keyed Lock in .Net

查看:56
本文介绍了.Net中的键锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Azure Service Bus队列,在其中我接收到的范围是1到10条带有相同键"的消息.这些消息之一需要长时间运行才能处理.完成后,数据库将被更新,其他消息将对其进行检查.但是,与此同时,其他消息将被重新排队,以确保该过程不会丢失.

I have a Azure Service Bus queue where I'm receiving a range from 1 to 10 messages with the same "key". One of these messages needs to be processed with a long running operation. After it's complete, the database will be updated and the other messages will check it. However, in the mean time, the other messages will be re-queued so that the process isn't lost.

但是要点是,对于同一密钥,该长时间运行的操作不能可以同时运行,并且不应多次运行.

But the main point is that this long running operation CANNOT be ran at the same time for the same key, and shouldn't be ran multiple times.

这是我到目前为止所得到的:

This is what I've got so far:

void Main()
{
    Enumerable.Range(1, 1000)
              .AsParallel()
              .ForAll(async i => await ManageConcurrency(i % 2, async () => await Task.Delay(TimeSpan.FromSeconds(10)))); 
}

private readonly ConcurrentDictionary<int, SemaphoreSlim> _taskLocks = new ConcurrentDictionary<int, SemaphoreSlim>();

private async Task<bool> ManageConcurrency(int taskId, Func<Task> task)
{
    SemaphoreSlim taskLock = null;

    try
    {
        if (_taskLocks.TryGetValue(taskId, out taskLock))
        {
            if (taskLock.CurrentCount == 0)
            {
                Console.WriteLine($"{DateTime.Now.ToString("hh:mm:ss.ffffff")},  {taskId}, I found. No available.. Thread Id: {Thread.CurrentThread.ManagedThreadId}");
                return false;
            }

            taskLock.Wait();

            Console.WriteLine($"{DateTime.Now.ToString("hh:mm:ss.ffffff")},  {taskId}, I found and took. Thread Id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
        }
        else
        {
            taskLock = new SemaphoreSlim(1, 1);
            taskLock = _taskLocks.GetOrAdd(taskId, taskLock);
            if (taskLock.CurrentCount == 0)
            {
                Console.WriteLine($"{DateTime.Now.ToString("hh:mm:ss.ffffff")},  {taskId}, I didn't find, and then found/created. None available.. Thread Id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
                return false;
            }
            else
            {
                taskLock.Wait(TimeSpan.FromSeconds(1));

                Console.WriteLine($"{DateTime.Now.ToString("hh:mm:ss.ffffff")},  {taskId}, I didn't find, then found/created, and took. Thread Id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
            }
        }

        Console.WriteLine($"{DateTime.Now.ToString("hh:mm:ss.ffffff")},  {taskId}, Lock pulled for TaskId {taskId}, Thread Id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");

        await task.Invoke();

        return true;
    }
    catch (Exception e)
    {
        ;
        return false;
    }
    finally
    {
        //taskLock?.Release();

        _taskLocks.TryRemove(taskId, out taskLock);

        //Console.WriteLine($"I removed. Thread Id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
    }
}

它没有按预期方式工作,因为它将创建多个信号量,并且突然我的长时间运行的操作使用相同的键运行了两次.我认为问题是因为整个操作不是原子的.

It's not working as expected, because it will create multiple semaphores and suddenly my long running operation is being ran twice with the same key. I think the problem is because the whole operation isn't atomic.

解决此问题的最佳方法是什么?

What's the best way to solve this issue?

推荐答案

您正确地认识到,您需要确保每个键仅创建一个信号灯.的标准习惯用法是:

You correctly recognized that you need to ensure that only one semaphore is being created per key. The standard idiom for that is:

var dict = new ConcurrentDictionary<TKey, Lazy<SemaphoreSlim>>();
...
var sem = dict.GetOrAdd( , _ => new new Lazy<SemaphoreSlim>(() => SemaphoreSlim(1, 1))).Value;

可能会创建多个懒惰,但其中只有一个会被发现和实现.

Multiple lazies might be created but only one of them will ever be revealed and materialized.

此外,依赖于内存中状态是一种可疑的做法.如果您的队列处理应用程序被回收并且所有信号灯都丢失了怎么办?您最好使用持久性存储来跟踪此锁定信息.

Besides that it is a questionable practice to rely on in-memory state. What if your queue processing app recycles and all semaphores are lost? You better use a persistent store to track this locking info.

这篇关于.Net中的键锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆