Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Net中的键控锁_C#_.net_Azure_Azureservicebus - Fatal编程技术网

C# Net中的键控锁

C# Net中的键控锁,c#,.net,azure,azureservicebus,C#,.net,Azure,Azureservicebus,我有一个Azure服务总线队列,在那里我接收到1到10条具有相同“密钥”的消息。其中一条消息需要通过长时间运行的操作进行处理。完成后,数据库将被更新,其他消息将对其进行检查。但是,同时,其他消息将重新排队,以便进程不会丢失 但是主要的一点是,这个长时间运行的操作不能针对同一个键同时运行,也不应该多次运行 到目前为止,我得到的是: void Main() { Enumerable.Range(1, 1000) .AsParallel()

我有一个Azure服务总线队列,在那里我接收到1到10条具有相同“密钥”的消息。其中一条消息需要通过长时间运行的操作进行处理。完成后,数据库将被更新,其他消息将对其进行检查。但是,同时,其他消息将重新排队,以便进程不会丢失

但是主要的一点是,这个长时间运行的操作不能针对同一个键同时运行,也不应该多次运行

到目前为止,我得到的是:

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}");
    }
}
void Main()
{
可枚举范围(11000)
.天冬酰胺()
.ForAll(异步i=>await ManageConcurrency(i%2,异步()=>await Task.Delay(TimeSpan.FromSeconds(10)));
}
私有只读ConcurrentDictionary _taskLocks=新建ConcurrentDictionary();
专用异步任务管理器并发性(int-taskId,Func-Task)
{
信号量lim taskLock=null;
尝试
{
if(_taskLocks.TryGetValue(taskId,out taskLock))
{
if(taskLock.CurrentCount==0)
{
WriteLine($“{DateTime.Now.ToString(“hh:mm:ss.ffffff”)},{taskId},我找到了。没有可用的..线程Id:{Thread.CurrentThread.ManagedThreadId}”);
返回false;
}
taskLock.Wait();
WriteLine($“{DateTime.Now.ToString(“hh:mm:ss.ffffff”)},{taskId},我找到并获取了.Thread Id:{System.Threading.Thread.CurrentThread.ManagedThreadId}”);
}
其他的
{
taskLock=新信号量lim(1,1);
taskLock=\u taskLocks.GetOrAdd(taskId,taskLock);
if(taskLock.CurrentCount==0)
{
WriteLine($“{DateTime.Now.ToString(“hh:mm:ss.ffffff”)},{taskId},我没有找到,然后找到/创建了。无可用..线程Id:{System.Threading.Thread.CurrentThread.ManagedThreadId}”);
返回false;
}
其他的
{
taskLock.Wait(TimeSpan.FromSeconds(1));
WriteLine($“{DateTime.Now.ToString(“hh:mm:ss.ffffff”)},{taskId},我没有找到,然后找到/创建,并获取.Thread Id:{System.Threading.Thread.CurrentThread.ManagedThreadId}”);
}
}
WriteLine($“{DateTime.Now.ToString(“hh:mm:ss.ffffff”)},{taskId},为taskId{taskId}拉锁,线程Id:{System.Threading.Thread.CurrentThread.ManagedThreadId}”);
等待任务。调用();
返回true;
}
捕获(例外e)
{
;
返回false;
}
最后
{
//taskLock?.Release();
_taskLocks.TryRemove(taskId,out taskLock);
//WriteLine($“I removed.Thread Id:{System.Threading.Thread.CurrentThread.ManagedThreadId}”);
}
}
它不能像预期的那样工作,因为它将创建多个信号量,突然我的长时间运行的操作用同一个键运行了两次。我认为问题在于整个操作不是原子的


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

你就快到了。。。您需要保留进货订单吗?如果没有:

public static void Main(string[] args)
{
    Enumerable.Range(1, 1000)
                .AsParallel()
                .ForAll( i => ManageConcurrency(i % 2,  () => Task.Delay(TimeSpan.FromSeconds(10))).Wait());


}

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

private static async Task<bool> ManageConcurrency(int taskId, Func<Task> task)
{

    var gate = _lockDict.GetOrAdd(taskId, _ => new SemaphoreSlim(1, 1));
    await gate.WaitAsync();

    try
    {

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

        await task();

        return true;
    }
    catch (Exception e)
    {
        return false;
    }
    finally
    {
        gate.Release();
    }

}
publicstaticvoidmain(字符串[]args)
{
可枚举范围(11000)
.天冬酰胺()
.ForAll(i=>ManageConcurrency(i%2,()=>Task.Delay(TimeSpan.FromSeconds(10))).Wait());
}
私有静态只读ConcurrentDictionary _lockDict=新ConcurrentDictionary();
专用静态异步任务管理器并发性(int taskId,Func Task)
{
var gate=_lockDict.GetOrAdd(taskId,=>newsemaphoreslim(1,1));
wait gate.WaitAsync();
尝试
{
WriteLine($“{DateTime.Now.ToString(“hh:mm:ss.ffffff”)},{taskId},为taskId{taskId}拉锁,线程Id:{System.Threading.Thread.CurrentThread.ManagedThreadId}”);
等待任务();
返回true;
}
捕获(例外e)
{
返回false;
}
最后
{
gate.Release();
}
}

您正确地认识到需要确保每个密钥只创建一个信号量。这方面的标准习惯用法是:

var dict = new ConcurrentDictionary<TKey, Lazy<SemaphoreSlim>>();
...
var sem = dict.GetOrAdd( , _ => new new Lazy<SemaphoreSlim>(() => SemaphoreSlim(1, 1))).Value;
var dict=新的ConcurrentDictionary();
...
var sem=dict.GetOrAdd(,=>new-new-Lazy(()=>SemaphoreSlim(1,1))).Value;
可能会创建多个懒惰,但其中只有一个会被揭示和实现


除此之外,依赖内存状态是一种值得怀疑的做法。如果您的队列处理应用程序回收,并且所有信号量都丢失了怎么办?您最好使用持久存储来跟踪此锁定信息。

在我看来,由于担心信号量之类的问题,您的生活变得更加困难。有更容易使用的抽象

在这种情况下,使用
Lazy
是理想的选择,但由于您希望等待结果,因此
Lazy
需要升级到
AsyncLazy

以及需要运行以执行计算的方法:

private LongRunningResult ComputeLongRunningResult(int index)
{
    Console.WriteLine($"Running Index {index}");
    Thread.Sleep(1000);
    return new LongRunningResult() { Index = index };
}
现在我们需要字典来保存惰性异步:

private readonly ConcurrentDictionary<int, AsyncLazy<LongRunningResult>> _results
    = new ConcurrentDictionary<int, AsyncLazy<LongRunningResult>>();

谢谢你的回答。我将尝试一下,看看它在我的环境中是如何工作的。为了回答您的问题,如果我的应用程序回收了,那么我必须重新启动长时间运行的操作,从scratchRelated:
private LongRunningResult ComputeLongRunningResult(int index)
{
    Console.WriteLine($"Running Index {index}");
    Thread.Sleep(1000);
    return new LongRunningResult() { Index = index };
}
private readonly ConcurrentDictionary<int, AsyncLazy<LongRunningResult>> _results
    = new ConcurrentDictionary<int, AsyncLazy<LongRunningResult>>();
Enumerable
    .Range(1, 10)
    .AsParallel()
    .ForAll(async i =>
    {
        var index = i % 2;
        Console.WriteLine($"Trying Index {index}");
        _results.TryAdd(index,
            new AsyncLazy<LongRunningResult>(
                () => ComputeLongRunningResult(index),
                LazyThreadSafetyMode.ExecutionAndPublication));
        AsyncLazy<LongRunningResult> ayncLazy;
        if (_results.TryGetValue(index, out ayncLazy))
        {
            await ayncLazy;
        }
    });
Trying Index 1
Trying Index 0
Trying Index 1
Trying Index 1
Trying Index 0
Trying Index 1
Running Index 1
Trying Index 0
Trying Index 1
Running Index 0
Trying Index 0
Trying Index 0