C# “我该怎么做?”;取消“;ConcurrentDictionary中的AddOrUpdate?
我已经阅读了MSDN文档,需要以下逻辑: 对于C# “我该怎么做?”;取消“;ConcurrentDictionary中的AddOrUpdate?,c#,.net,multithreading,concurrency,concurrentdictionary,C#,.net,Multithreading,Concurrency,Concurrentdictionary,我已经阅读了MSDN文档,需要以下逻辑: 对于ConcurrentDictionary 如果字符串不存在,请添加它,并确保在添加时将bool设置为True 如果字符串确实存在,则仅当布尔值为false时才将其更改为True。否则取消更新 我的用例 我有几个DNS域来扫描恶意软件。在我实时检索的列表中,很可能会有重复项。我收到的DNS域名列表批量为100个或更少,将有超过10000个域名进行扫描 我只想在10000个域的每次迭代中扫描一次DNS主机。Abool==true表示当前正在扫描它,我应该
ConcurrentDictionary
True
True
。否则取消更新bool==true
表示当前正在扫描它,我应该在继续之前取消该任务。bool==false
或无条目意味着我应该立即将条目更新为bool==true
或尽快创建一个新条目
记住……
AddOrUpdate将独立于.NET4的TPL中的许多独立线程进行调用。每个线程需要决定是否需要处理字典的键中提到的值。。。或者继续下一个。只有一把“钥匙”应该对它进行操作
我需要向调用线程发出更新成功或失败的信号。此外,AddOrUpdate的函数似乎会被多次调用。我想这可能意味着我的调用线程将被混淆,无法取消键上的工作或继续它。(请记住,只有一个线程可以在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();
});
});
问题
如何将此“取消更新”功能添加到AddOrUpdate?使用该博客文章中提到的AddOrUpdate方法。在AddDelgate中,将bool设置为true。在update委托中,让它检查作为参数传递给委托的bool值,并始终返回true。我这样说是因为
- 如果为false,则将其设置为true
- 如果是真的,取消更新(即保持为真)。因此,您不妨将其设置为真
如果缺少某些其他条件,请详细说明。您可以按照以下方法进行操作:
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
中完成整个任务的方法。如果我理解您试图实现的目标,我认为您不能使用ConcurrentDictionary
一种可能的解决方案是使用一个类来封装给定主机的扫描:
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
您将按如下方式使用它:
Scanner s = dictionary.GetOrAdd(host, new Lazy<Scanner>(() => new Scanner(host));
s.StartScanning();
Scanner s=dictionary.GetOrAdd(主机,新延迟(()=>newscanner(主机));
s、 开始扫描();
Lazy
实例将使用默认的LazyThreadSafetyMode.ExecutionAndPublication
模式,这意味着只有一个线程会调用工厂委托来实例化给定主机的扫描程序
从我对您问题的理解来看,我认为这就是您要实现的目标,即不要多次扫描同一主机。尝试使用ConcurrentDictionary>
创建Lazy时,传入一个在站点上运行扫描的委托。第一次访问Lazy.Value属性时,将运行扫描。在第一次扫描完成之前,所有后续调用方都将被阻止。扫描完成后,访问Lazy.Value的任何人都将获得该值,但不会运行第二次扫描。ConcurrentDictionary
的并发性使其无法工作
您真正需要对字典中已有的值执行操作的唯一机会是updateValueFactory
,但该工作将在更新实际发生之前进行,并且该值设置为true
。在此期间,另一个线程也可能尝试添加或更新
,在这种情况下,它仍会看到将旧值设置为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
时(当调用UpdateLogic
时)与实际执行时之间的延迟。在此期间,即在值更新为true
之前,updateValueFactory
再次调用0,这将导致UpdateLogic代码>也将再次为0运行
你需要某种类型的锁来确保读取值、调用更新逻辑和设置新值都是一个原子操作。将一个已经为True
的值设置为True
不是一个不可操作的操作吗?为什么要取消呢?@Blorgbeard,如果你指的是#1,我的意思是,但我不够明确。我更新了项目符号并添加了一个用于澄清的用例。我在问题中添加了更多信息…我如何与调用线程通信,说明当前正在由对等线程进行工作(bool已经为true
)或者AddOrUpdate起作用了,这是调用线程做一些工作的机会。从代码的角度来看,这更干净。您认为查找次数可能会更少……这种方法或将bool
包装在一个对象中,该对象也具有ThreadID和DateTime
(用于计算锁的持续时间)…然后不更改值
。当然,这里列出的同步更新。。。
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