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;
}
}