3

MSDN ドキュメントとこのブログを読みましたが、次のロジックが必要です。

のためにConcurrentDictionary<string,bool>

  1. 文字列が存在しない場合は追加し、追加True中にブール値を設定していることを確認してください
  2. 文字列が存在する場合は、Trueそれが false の場合にのみ bool を変更します。それ以外の場合は、更新をキャンセルします

私のユースケース

マルウェアをスキャンする DNS ドメインがいくつかあります。私がリアルタイムで取得するリストには重複がある可能性が高いです。DNS ドメインのリストを 100 以下のバッチで受け取ります。スキャンするドメインは 10,000 を超えます。

10,000 ドメインの反復ごとに 1 回だけ DNS ホストをスキャンしたいと考えています。Abool == trueは、現在スキャン中であり、先に進む前にタスクをキャンセルする必要があることを意味します。bool == falseエントリがない場合は、すぐにエントリを更新するか、できるだけ早く新しいエントリを作成する必要があることを意味しますbool==true

覚えておいてください...

AddOrUpdate は、.NET4 の TPL の多くの独立したスレッドから独立して呼び出されます。key各スレッドは、ディクショナリの...に記載されている値で作業する必要があるか、次の値に進む必要があるかを決定する必要があります。1 つの「キー」だけが作業を行う必要があります。

更新が成功したか失敗したかを呼び出し元のスレッドに通知する必要があります。さらに、この回答によると、AddOrUpdate の関数が何度も呼び出されるようです。keyこれは、呼び出しスレッドが作業をキャンセルするか続行するかについて混乱することを意味する可能性があると思います。(アクティブに作業できるスレッドは 1 つだけであることを覚えておいてください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>これには使用できないと思います。

考えられる解決策の1つは、特定のホストのスキャンをカプセル化するクラスを作成することです。

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つまり、特定のホストのスキャナーをインスタンス化するためにファクトリデリゲートを呼び出すスレッドは1つだけです。

あなたの質問の私の理解から、これはあなたが達成しようとしていることであるように私には見えます。つまり、同じホストを複数回スキャンしないでください。

于 2012-11-19T00:58:48.520 に答える
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'
}

もちろん、それは2倍のキールックアップを引き起こします..しかし、私はすべてのことをConcurrentDictionary.

于 2012-11-19T00:11:45.567 に答える
1

そのブログ投稿で説明されている AddOrUpdate メソッドを使用します。add デリゲートで、bool を true に設定します。update デリゲートで、パラメーターとしてデリゲートに渡された bool 値をチェックし、常に true を返すようにします。言ってるから言うんだよ

  • false の場合は true に設定します
  • true の場合、更新をキャンセルします (つまり、true のままにします)。したがって、trueに設定することもできます

他に欠落している条件がある場合は、詳しく説明してください。

于 2012-11-18T23:20:15.400 に答える
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てから、実際に実行されるまでの遅延に注意してください。この間、つまり値が に更新される前に がtrue再度updateValueFactory0 に対して呼び出され、これによりUpdateLogicも 0 に対して再度実行されます。

値の読み取り、更新ロジックの呼び出し、新しい値の設定がすべて 1 つのアトミック操作であることを確認するには、何らかのロックが必要です。

于 2015-03-26T03:09:07.100 に答える