3

我已经阅读了 MSDN 文档和这个博客,我需要以下逻辑:

为一个ConcurrentDictionary<string,bool>

  1. 如果字符串不存在,添加它,并确保我True在添加时将bool 设置为
  2. 如果字符串确实存在,则仅将 bool 更改为Truefalse 。否则取消更新

我的用例

我有几个 DNS 域来扫描恶意软件。我实时检索的列表中很可能会有重复项。我以 100 个或更少的批次收到 DNS 域列表,并且将有超过 10,000 个域进行扫描。

我只想在每次迭代 10,000 个域时扫描一次 DNS 主机。Abool == true表示它当前正在被扫描,我应该在继续之前取消该任务。有bool == false或没有条目意味着我应该立即将条目更新为bool==true或尽快创建一个新条目。

记住...

AddOrUpdate 将独立于 .NET4 的 TPL 中的许多独立线程调用。每个线程都需要决定它是否需要处理 Dictionary's key... 中提到的值或继续下一个。只有一个“钥匙”应该对其进行处理。

我需要向调用线程发出更新成功或失败的信号。另外根据这个答案,AddOrUpdate 的函数似乎会被调用很多次。我认为这可能意味着我的调用线程会因为取消工作key或继续工作而感到困惑。(记住只有一个线程可以积极工作key

可能混淆调用线程的并发更新示例

ConcurrentDictionary<int, string> numbers = new ConcurrentDictionary<int, string>();
Parallel.For(0, 10, x =>
{
    numbers.AddOrUpdate(1,
        i =>
        {
            Console.WriteLine("addValueFactory has been called");
            return i.ToString();
        },
        (i, s) =>
        {
            Console.WriteLine("updateValueFactory has been called");
            return i.ToString();
        });
});

输出

addValueFactory has been called
addValueFactory has been called
addValueFactory has been called
addValueFactory has been called
updateValueFactory has been called
updateValueFactory has been called
updateValueFactory has been called
updateValueFactory has been called
updateValueFactory has been called
updateValueFactory has been called
updateValueFactory has been called
updateValueFactory has been called
updateValueFactory has been called

问题

我应该如何将此“取消更新”功能添加到 AddOrUpdate?

4

5 回答 5

4

如果我了解您要实现的目标,我认为您不能ConcurrentDictionary<string, bool>为此使用 a 。

一种可能的解决方案是拥有一个封装给定主机扫描的类:

public class Scanner
{
    private static _syncRoot = new object();

    public Scanner(string host)
    {
        Host = host;
        StartScanning();
    }

    public string Host {get; private set; }

    public bool IsScanning {get; private set; }

    public void StartScanning()
    {
        lock(_syncRoot)
        {
            if (!IsScanning)
            {
                IsScanning = true;
                // Start scanning Host asynchronously
                ...
            }
        }
    }

    private void EndScanning()
    {
        // Called when asynchronous scanning has completed
        IsScanning = false;
    }
}

然后是字典ConcurrentDictionary<string, Lazy<Scanner>>

您可以按如下方式使用它:

Scanner s = dictionary.GetOrAdd(host, new Lazy<Scanner>(() => new Scanner(host));
s.StartScanning();

Lazy<Scanner>实例将使用默认LazyThreadSafetyMode.ExecutionAndPublication模式,这意味着只有一个线程会调用工厂委托来实例化给定主机的 Scanner。

根据我对您的问题的理解,在我看来这就是您要实现的目标,即不要多次扫描同一主机。

于 2012-11-19T00:58:48.520 回答
1

使用该博客文章中提到的 AddOrUpdate 方法。在您的 add delgate 中,将 bool 设置为 true。在您的更新委托中,让它检查作为参数传递给委托的布尔值并始终返回 true。我这么说是因为你在说

  • 如果为假,则设置为真
  • 如果为真,则取消更新(即保留为真)。所以你不妨将它设置为 true

如果缺少其他条件,请详细说明。

于 2012-11-18T23:20:15.400 回答
1

您可以按照以下方式做一些事情:

if (dic.TryAdd(domain, true)) || (dic.TryUpdate(domain, true, false)) {
   // this thread just added a new 'true' entry, 
   // or changed an existing 'false' entry to 'true'
}

当然,它会导致两倍的键查找。但我看不出有一种方法可以在ConcurrentDictionary.

于 2012-11-19T00:11:45.567 回答
0

尝试使用 ConcurrentDictionary>。

创建 Lazy 时,传入一个在站点上运行扫描的委托。第一次访问您的 Lazy.Value 属性时,将运行扫描。任何后续调用者都将被阻止,直到第一次扫描完成。扫描完成后,访问 Lazy.Value 的任何人都将获得该值,但永远不会运行第二次扫描。

于 2013-10-08T21:51:23.633 回答
0

的并发性ConcurrentDictionary使这不起作用。

您真正必须对字典中已有的值采取行动的唯一机会是在 中updateValueFactory,但该工作将更新实际发生并且值设置为之前进行true。在此期间,另一个线程也可能会尝试AddOrUpdate,在这种情况下,它仍然会看到 的旧值false,并再次启动更新逻辑。

这是一个示例程序来演示这一点:

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace ConcurrentDictionaryCancelTest {
    class Program {
        static void Main( string[] args ) {
            var example = new ConcurrentDictionary<string, bool>();

            for( var i = 0; i < 3; i++ ) {
                example.AddOrUpdate( i.ToString(), false, ( key, oldValue ) => false );
            }

            Parallel.For( 0, 8, x => {
                example.AddOrUpdate(
                    ( x % 3 ).ToString(),
                    ( key ) => {
                        Console.WriteLine( "addValueFactory called for " + key );
                        return true;
                    },
                    ( key, oldValue ) => {
                        Console.WriteLine( "updateValueFactory called for " + key );
                        if( !oldValue ) {
                            var guid = Guid.NewGuid();
                            Console.WriteLine( 
                                key + " is calling UpdateLogic: " + guid.ToString() 
                            );
                            UpdateLogic( key, guid );
                        }
                        return true;
                    }
                );
            } );
        }

        public static void UpdateLogic( string key, Guid guid ) {
            Console.WriteLine( 
                "UpdateLogic has been called for " + key + ": " + guid.ToString()
            );
        }
    }
}

还有一些示例输出:

updateValueFactory called for 0
updateValueFactory called for 1
updateValueFactory called for 2
updateValueFactory called for 0
updateValueFactory called for 1
0 is calling UpdateLogic: cdd1b1dd-9d96-417d-aee7-4c4aec7fafbf
1 is calling UpdateLogic: 161c5f35-a2d7-44bf-b881-e56ac713b340
UpdateLogic has been called for 0: cdd1b1dd-9d96-417d-aee7-4c4aec7fafbf
updateValueFactory called for 1
1 is calling UpdateLogic: 6a032c22-e8d4-4016-a212-b09e41bf4d68
UpdateLogic has been called for 1: 6a032c22-e8d4-4016-a212-b09e41bf4d68
updateValueFactory called for 0
updateValueFactory called for 2
2 is calling UpdateLogic: 76c13581-cd55-4c88-961c-12c6d277ff00
UpdateLogic has been called for 2: 76c13581-cd55-4c88-961c-12c6d277ff00
1 is calling UpdateLogic: d71494b6-265f-4ec8-b077-af5670c02390
UpdateLogic has been called for 1: d71494b6-265f-4ec8-b077-af5670c02390
UpdateLogic has been called for 1: 161c5f35-a2d7-44bf-b881-e56ac713b340
updateValueFactory called for 1
updateValueFactory called for 1
0 is calling UpdateLogic: f6aa3460-444b-41eb-afc6-3d6afa2f6512
UpdateLogic has been called for 0: f6aa3460-444b-41eb-afc6-3d6afa2f6512
2 is calling UpdateLogic: d911dbd1-7150-4823-937a-26abb446c669
UpdateLogic has been called for 2: d911dbd1-7150-4823-937a-26abb446c669
updateValueFactory called for 0
updateValueFactory called for 2

请注意,第一次updateValueFactory调用为 0,何时UpdateLogic调用,然后它实际执行之间的延迟。在此期间,即在值更新为 之前trueupdateValueFactory再次调用 0,这UpdateLogic也会导致再次为 0 运行。

您需要某种锁来确保读取值、调用更新逻辑和设置新值都是一个原子操作。

于 2015-03-26T03:09:07.100 回答