HashSet <t> .Contains()对大型列表,多线程有效吗?</t>

时间:2013-08-01 03:50:51

标签: c# .net multithreading performance hashset

我正在编写一个多线程程序来抓取某个站点并收集ID。它将这些ID存储在共享的静态List<string>对象中。

当任何项目添加到List<string>时,首先会针对包含已收集ID的黑名单的HashSet<string>进行检查。

我这样做如下:

private static HashSet<string> Blacklist = new HashSet<string>();
private static List<string> IDList = new List<string>();

public static void AddIDToIDList(string ID)
{
    lock (IDList)
    {
        if (IsIDBlacklisted(ID))
            return;
        IDList.Add(ID);
    }
}
public static bool IsIDBlacklisted(string ID)
{
    lock (Blacklist)
    {
        if (Blacklist.Contains(ID))
            return true;
    }
    return false;
 }

黑名单在完成后保存到文件中,并在每次程序启动时加载,因此,随着时间的推移它会变得非常大(最多50k记录)。有没有更有效的方法来不仅存储这个黑名单,还要检查每个ID?

谢谢!

4 个答案:

答案 0 :(得分:3)

要提高性能,请尝试使用ConcurrentBag<T>集合。此外,没有必要锁定BlackList,因为它没有被修改,例如:

private static HashSet<string> Blacklist = new HashSet<string>();
private static ConcurrentBag<string> IDList = new ConcurrentBag<string>();

public static void AddIDToIDList(string ID)
{
    if (Blacklist.Contains(ID))
    {
        return;
    }

    IDList.Add(ID);
}

答案 1 :(得分:2)

读取操作在HashSet上是线程安全的,只要Blacklist未被修改,您就不需要锁定它。此外,你应该锁定黑名单检查,以减少锁定,这也会提高你的表现。

private static HashSet<string> Blacklist = new HashSet<string>();
private static List<string> IDList = new List<string>();

public static void AddIDToIDList(string ID)
{
    if (IsIDBlacklisted(ID))
        return;
    lock (IDList)
    {
        IDList.Add(ID);
    }
}
public static bool IsIDBlacklisted(string ID)
{
    return Blacklist.Contains(ID);
}

如果正在修改Blacklist,锁定它的最佳方法是使用ReaderWriterLock(如果您使用较新的.NET,请使用slim version

private static HashSet<string> Blacklist = new HashSet<string>();
private static List<string> IDList = new List<string>();
private static ReaderWriterLockSlim BlacklistLock = new ReaderWriterLockSlim();

public static void AddIDToIDList(string ID)
{
    if (IsIDBlacklisted(ID))
        return;
    lock (IDList)
    {
        IDList.Add(ID);
    }
}
public static bool IsIDBlacklisted(string ID)
{
    BlacklistLock.EnterReadLock();
    try
    {
        return Blacklist.Contains(ID);
    }
    finally
    {
        BlacklistLock.ExitReadLock();
    }
}

public static bool AddToIDBlacklist(string ID)
{
    BlacklistLock.EnterWriteLock();
    try
    {
        return Blacklist.Add(ID);
    }
    finally
    {
        BlacklistLock.ExitWriteLock();
    }
}

答案 2 :(得分:1)

两个注意事项 - 首先,如果您使用.NET字典的索引器(即System.Collections.Generic.Dictionary)(而不是调用Add()方法):

idList[id] = id;

然后它将添加项目(如果它尚不存在) - 否则,它将替换该键的现有项目。其次,您可以使用ConcurrentDictionary(在System.Collections.Concurrent命名空间中)进行线程安全,因此您不必担心自己锁定。有关使用索引器的相同注释。

答案 3 :(得分:1)

在您的方案中,是的, HashSet是此的最佳选项,因为它包含一个要查找的值,而不像 Dictionary 那需要键和值来执行查找。

当然,正如其他人所说,如果没有被修改,就不需要锁定HashSet。并考虑将其标记为只读。