C# 如果具有相同ID的对象当前正在由另一个任务处理,则延迟对象的任务处理

C# 如果具有相同ID的对象当前正在由另一个任务处理,则延迟对象的任务处理,c#,async-await,task,C#,Async Await,Task,假设我有对象: ObjectA( int ID, List<ObjectB> items ) ObjectB( int ItemId, string value ) 对每个ObjectA进行反序列化后,我运行两个任务,taskA和taskB,这两个任务目前并不重要 重要的是处理ObjectA的所有项目的TaskA-对象b的列表taskA还为每个ObjectB运行多个任务 如何确保在所有任务taskA或当前收到的所有RMQ消息中,只有一个ObjectB具有相同的ItemId的消息可

假设我有对象:

ObjectA(
int ID,
List<ObjectB> items
)

ObjectB(
int ItemId,
string value
)
对每个
ObjectA
进行反序列化后,我运行两个任务,
taskA
taskB
,这两个任务目前并不重要

重要的是处理
ObjectA
的所有项目的
TaskA
-对象b的列表
taskA
还为每个
ObjectB
运行多个任务

如何确保在所有任务
taskA
或当前收到的所有RMQ消息中,只有一个
ObjectB
具有相同的
ItemId
的消息可以同时处理?

示例

ObjectA(Id=1,Items={(1,“a”),(2,“b”),(1,“c”)})


对于给定的对象,我必须开始并行处理项目
(1,“a”)
(2,“b”)
,而
(1,“c”)
应该等到正在处理的任务
(1,“a”)
完成处理,因为它们的
项目ID
是相同的。

如果使用容器,则需要注册为单例

在本例中,将为每个密钥创建一个锁,并且在前一个任务正在运行时不会启动新任务

public sealed class DelayTaskProcessor
{
    private readonly Dictionary<string, object> _syncDict = new Dictionary<string, object>();
    private readonly object _sync = new object();

    public Task Create(Action action, string id)
    {
        return new Task(() =>
        {

            object currentSync = null;
            lock (_sync)
            {
                if (_syncDict.ContainsKey(id))
                {
                    currentSync = _syncDict[id];
                }
                else
                {
                    currentSync = new object();
                    _syncDict.Add(id, currentSync);
                }
            }

            lock (currentSync)
            {
                Console.WriteLine($"Start: {id}");
                action?.Invoke();

            }
        });
    }
}
        public sealed class ObjectA
        {
            public int Id { get; set; }
            public List<ObjectB> Items { get; set; }
        }
        public sealed class ObjectB
        {
            public int Id { get; set; }
            public string Value { get; set; }
        }

    static void Main(string[] args)
    {
        var p = new DelayTaskProcessor();
        var objectA = new ObjectA
        {
            Id = 1, Items = new List<ObjectB>()
                {
                    new ObjectB {Id=1, Value="a"},
                    new ObjectB {Id=1, Value="a2"},
                    new ObjectB {Id=2, Value="b"},
                    new ObjectB {Id=3, Value="c"}
                }
            };
        var tasks = new List<Task>(objectA.Items.Count);
        foreach (var item in objectA.Items)
        {
            var task = p.Create(() =>
            {
                Console.WriteLine($"{item.Id},{item.Value}");
                Thread.Sleep(2000);

            }, $"b:{item.Id}");
            tasks.Add(task);
            task.Start();
        }

        Task.WaitAll(tasks.ToArray());
        var exceptions = new List<Exception>();
        foreach (var t in tasks)
        {
            if (t.IsFaulted)
            {
                exceptions.Add(t.Exception);
                
            }
            using (t) { }
        }
}
公共密封类处理器
{
专用只读词典_syncDict=new Dictionary();
私有只读对象_sync=新对象();
公共任务创建(操作操作,字符串id)
{
返回新任务(()=>
{
对象currentSync=null;
锁定(同步)
{
如果(_syncDict.ContainsKey(id))
{
currentSync=_syncDict[id];
}
其他的
{
currentSync=新对象();
_syncDict.Add(id,currentSync);
}
}
锁定(当前同步)
{
WriteLine($“Start:{id}”);
action?.Invoke();
}
});
}
}
公共密封类对象A
{
公共int Id{get;set;}
公共列表项{get;set;}
}
公共密封类ObjectB
{
公共int Id{get;set;}
公共字符串值{get;set;}
}
静态void Main(字符串[]参数)
{
var p=新的DelayTaskProcessor();
var objectA=新objectA
{
Id=1,Items=newlist()
{
新对象b{Id=1,Value=“a”},
新对象b{Id=1,Value=“a2”},
新对象b{Id=2,Value=“b”},
新对象b{Id=3,Value=“c”}
}
};
var tasks=新列表(objectA.Items.Count);
foreach(objectA.Items中的变量项)
{
var task=p.Create(()=>
{
WriteLine($“{item.Id},{item.Value}”);
《睡眠》(2000年);
},$“b:{item.Id}”);
任务。添加(任务);
task.Start();
}
Task.WaitAll(tasks.ToArray());
var exceptions=新列表();
foreach(任务中的var t)
{
如果(t.IsFaulted)
{
例外。添加(t.例外);
}
使用(t){}
}
}

我必须结合和的解决方案,对我有效的代码是:

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

    private sealed class RefCounted<T>
    {
        public RefCounted(T value)
        {
            RefCount = 1;
            Value = value;
        }

        public int RefCount { get; set; }
        public T Value { get; private set; }
    }

    public DelayTaskProcessor()
    {
        
    }

    public async Task<bool> ManageConcurrency(object taskId, Func<Task> task)
    {
        await GetOrCreate(taskId).WaitAsync();

        try
        {
            await task();

            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
        finally
        {
            RefCounted<SemaphoreSlim> item;
            lock (_lockDict)
            {
                item = _lockDict[taskId];
                --item.RefCount;
                if (item.RefCount == 0)
                    _lockDict.TryRemove(taskId, out var removed);
            }
            item.Value.Release();
        }
    }

    private SemaphoreSlim GetOrCreate(object key)
    {
        RefCounted<SemaphoreSlim> item;
        lock (_lockDict)
        {
            if (_lockDict.TryGetValue(key, out item))
            {
                ++item.RefCount;
            }
            else
            {
                item = new RefCounted<SemaphoreSlim>(new SemaphoreSlim(1, 1));
                _lockDict[key] = item;
            }
        }
        return item.Value;
    }
}
私有静态只读ConcurrentDictionary _lockDict=new ConcurrentDictionary();
私人密封类
{
公共参考值(T值)
{
RefCount=1;
价值=价值;
}
公共int RefCount{get;set;}
公共T值{get;私有集;}
}
公共处理器()
{
}
公共异步任务管理器并发(对象任务ID,Func任务)
{
等待GetOrCreate(taskId).WaitAsync();
尝试
{
等待任务();
返回true;
}
捕获(例外情况除外)
{
返回false;
}
最后
{
重计数项目;
锁
{
项目=_lockDict[taskId];
--项目1.RefCount;
如果(item.RefCount==0)
_lockDict.TryRemove(taskId,out-var已删除);
}
item.Value.Release();
}
}
私有信号量lim GetOrCreate(对象密钥)
{
重计数项目;
锁
{
如果(_lockDict.TryGetValue(键,输出项))
{
++项目1.RefCount;
}
其他的
{
item=新的RefCounted(新的信号量lim(1,1));
_lockDict[键]=项;
}
}
返回项。值;
}
}

请随意评论。

获取这些对象,添加到具有相同Id的其他列表/集合对象,然后逐个处理它们。因此,具有不同ID的对象将位于不同的集合中,并相互并行处理;但是具有相同Id的对象将按顺序处理。相关:您能给我提供创建任务的示例吗?我添加到了Answer中,因为我对此非常费劲,所以我没有更多的问题。如果我有5个任务要在foreach内部执行,我是否需要让p.Create。。。5次。另外,我如何知道当前项目的所有任务何时完成?我可以制作一些类似任务列表的东西,然后只使用Task.whalll(listOfTasks)吗?我添加了任务列表来回答和等待
    private static readonly ConcurrentDictionary<object, RefCounted<SemaphoreSlim>> _lockDict = new ConcurrentDictionary<object, RefCounted<SemaphoreSlim>>();

    private sealed class RefCounted<T>
    {
        public RefCounted(T value)
        {
            RefCount = 1;
            Value = value;
        }

        public int RefCount { get; set; }
        public T Value { get; private set; }
    }

    public DelayTaskProcessor()
    {
        
    }

    public async Task<bool> ManageConcurrency(object taskId, Func<Task> task)
    {
        await GetOrCreate(taskId).WaitAsync();

        try
        {
            await task();

            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
        finally
        {
            RefCounted<SemaphoreSlim> item;
            lock (_lockDict)
            {
                item = _lockDict[taskId];
                --item.RefCount;
                if (item.RefCount == 0)
                    _lockDict.TryRemove(taskId, out var removed);
            }
            item.Value.Release();
        }
    }

    private SemaphoreSlim GetOrCreate(object key)
    {
        RefCounted<SemaphoreSlim> item;
        lock (_lockDict)
        {
            if (_lockDict.TryGetValue(key, out item))
            {
                ++item.RefCount;
            }
            else
            {
                item = new RefCounted<SemaphoreSlim>(new SemaphoreSlim(1, 1));
                _lockDict[key] = item;
            }
        }
        return item.Value;
    }
}