Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/heroku/2.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# 修改值时在ConcurrentDictionary中循环_C#_Task Parallel Library_Concurrentdictionary - Fatal编程技术网

C# 修改值时在ConcurrentDictionary中循环

C# 修改值时在ConcurrentDictionary中循环,c#,task-parallel-library,concurrentdictionary,C#,Task Parallel Library,Concurrentdictionary,我在static类中有一个静态ConcurrentDictionary。在类的静态构造函数中,我通过Task调用一个私有方法。运行无限期地循环字典并删除已过期的项,即,自添加这些项以来已经过了很长时间,因此需要删除它们(这是通过检查字典中每个项目的CustomItem值上的属性来确定的): 公共静态类ItemsHolder { 公共静态ConcurrentDictionary项=新建ConcurrentDictionary(); // ... 静态项目文件夹 { Task.Run(()=>Del

我在
static
类中有一个静态
ConcurrentDictionary
。在类的静态构造函数中,我通过
Task调用一个私有方法。运行
无限期地循环字典并删除已过期的项,即,自添加这些项以来已经过了很长时间,因此需要删除它们(这是通过检查字典中每个项目的
CustomItem
值上的属性来确定的):

公共静态类ItemsHolder
{
公共静态ConcurrentDictionary项=新建ConcurrentDictionary();
// ...
静态项目文件夹
{
Task.Run(()=>DeleteExpired());
}
私有静态异步任务DeleteExpired()
{
while(true)
{
//检查字典是否为空,如果为空,
//等待任务。延迟一点等-为简洁起见省略
foreach(项目中的var项目)
{
var guid=item.Key;
var customItem=项目值;
if(customItem.ConditionToRemoveItemFromDictionaryHere)
{
var removietem=Items.TryRemove(guid,out);
// ...
}
}
}
}
}
在代码的其他部分(该类之外),项将被添加到此字典中,并根据其他条件删除。实际上,
DeleteExpired()
用于清除无法从字典中删除的项(在代码的其他部分,出于XYZ原因)


我看到
DeleteExpired()
由于占用CPU时间而不断出现在我的工具(如R#中的dotTrace)中。我知道,在其他线程可能正在添加/删除的情况下,通过并发字典进行迭代是无锁的,应该是安全的,但我不确定这是否是最有效的方法。是否有更有效/更高效的doi方法那么呢?

如果字典不是空的,那么在每次迭代之间就不需要等待了

while(true)
使线程疯狂地旋转,不停地旋转(非常准确)。在每次检查之间,您必须绝对等待

在没有延迟或睡眠的情况下,一个线程将持续消耗整个CPU核心(或者至少它将尝试这样做),这就是性能异常的原因

我会这样写:

while(true) {
  Items.Where(pair => pair.Value.ConditionToRemoveItemFromDictionaryHere)
    .Select(pair => pair.Key)
    .ToList()
    .ForEach(guid => Items.TryRemove(guid, out _));

  // thread.sleep, task delay or whatever suits your needs
}

我会建议使用后台任务调度器,比如Quartz,因为调度器是专门为该用例设计的

创建清理字典的任务:

public static class ItemsHolder
{
    public static ConcurrentDictionary<Guid, CustomItem> Items = new ConcurrentDictionary<Guid, CustomItem>();

   // ...

    static ItemsHolder
    {
        Task.Run(() => DeleteExpired());
    }

    private static async Task DeleteExpired()
    {
        while (true)
        {
            // check if dictionary is empty and if yes, 
            // await Task.Delay a bit, etc - omitted for brevity

            foreach (var item in Items)
            {
                var guid = item.Key;
                var customItem = item.Value;

                if (customItem.ConditionToRemoveItemFromDictionaryHere)
                {

                    var removeItem = Items.TryRemove(guid, out _);

                    // ...
                }
            }
        }
    }
}
public class CleanExpiredJob : IJob
{
    // Your ConcurrentDictionary value will be injected by the scheduler
    public ConcurrentDictionary<TKey, TValue> Items {private get; set;}
    public Task Execute(IJobExecutionContext context)
    {
        return Task.Run(() => {
            foreach (var item in Items)
            {
                var guid = item.Key;
                var customItem = item.Value;

                if (customItem.ConditionToRemoveItemFromDictionaryHere)
                {

                    var removeItem = Items.TryRemove(guid, out _);

                    // ...
                }
            }
        });
    }
}
公共类CleanExpiredJob:IJob
{
//您的ConcurrentDictionary值将由调度程序注入
公共ConcurrentDictionary项{private get;set;}
公共任务执行(IJobExecutionContext上下文)
{
返回任务。运行(()=>{
foreach(项目中的var项目)
{
var guid=item.Key;
var customItem=项目值;
if(customItem.ConditionToRemoveItemFromDictionaryHere)
{
var removietem=Items.TryRemove(guid,out);
// ...
}
}
});
}
}
然后,将计划程序添加到应用程序启动:

    // Grab the Scheduler instance from the Factory
    NameValueCollection props = new NameValueCollection
    {
        { "quartz.serializer.type", "binary" }
    };
    var factory = new StdSchedulerFactory(props);
    var scheduler = await factory.GetScheduler();

    await scheduler.Start();
    var job = JobBuilder.Create<CleanExpiredJob>().Build();

    // Add your dictionary here
    // Notice: Keep the same name as the property in the job for injection
    job.JobDataMap["Items"] = Items;

    // Set the job to run every 10 seconds
    var trigger = TriggerBuilder.Create()
                    .StartNow()
                    .WithSimpleSchedule(x => x
                        .WithIntervalInSeconds(1)
                        .RepeatForever())
                    .Build();

    scheduler.ScheduleJob(job, trigger);
//从工厂获取调度程序实例
NameValueCollection道具=新的NameValueCollection
{
{“quartz.serializer.type”,“binary”}
};
变量工厂=新的StdSchedulerFactory(props);
var scheduler=wait factory.GetScheduler();
wait scheduler.Start();
var job=JobBuilder.Create().Build();
//把你的字典加在这里
//注意:保持与用于注入的作业中的属性相同的名称
job.JobDataMap[“项”]=项;
//将作业设置为每10秒运行一次
var trigger=TriggerBuilder.Create()
.StartNow()
.使用SimpleSchedule(x=>x
.间隔秒(1)
.RepeatForever())
.Build();
ScheduleJob(作业,触发器);

您可以使用SortedDictionary按到期日期订购商品,并从列表的开头提取。这不是并发的,但您可以使用N个这样的列表并使用锁切分,这应该会有很大帮助。您好@usr,您能解释一下为什么要使用
N
这样的列表以及您所说的锁切分是什么意思吗?这是在执行时手动锁定集合吗ng对它的操作?@globetrotter-您不能从正在迭代的集合中删除项。它们是
并发的*
集合还是标准集合并不重要。对于单个线程来说是这样。对于多个线程来说更糟。如果只有一个列表,所有线程都会在该锁上竞争。可能您正在使用ConcurrentDi因为很多线程都有很多操作。但实际上,你可以通过使用一个简单的字典并锁定它来提高效率。如果写入频率不太高,这将使迭代速度大大加快。这取决于你的写入频率。但是锁切分是解决写入争用的一种方法。顺便说一句,你可以使用锁切分和N个字典实例。这样,删除迭代会更快,也会更快。检查字典是否为空,如果为空,请等待任务。延迟一点,等等-为简洁起见省略。是否意味着仅当字典为空时才等待?如果是,则会影响性能。那里有一个代码注释,其中he说他没有等待。这是问题的关键,因为尽管等待得很好,他还是有一个性能问题。@usr OP中有一条评论,我说“是的,我只在字典为空时异步等待,所以如果没有,我不会尝试用foreach枚举”.我的母语不是英语,但我想这意味着如果收藏不是空的,他不会等。哦,我明白了。