线程和列表,我如何遍历每个并进行比较

Threading and List, how do I go through each and compare

我正在尝试使用线程提取嵌套列表以与另一个列表进行比较,类似这样

List<KeyPairValues<string, List<string>> mainList = new List<KeyPairValues<string, List<string>>();

// In my actual code I create these list through a loop
List<string> A = new List<string>();
A.Add("Car");
A.Add("Plain");
A.Add("Boat");

List<string> B = new List<string>();
B.Add("Flower");
B.Add("Dog");
B.Add("House");

List<string> C = new List<string>();
C.Add("Appartment");
C.Add("Plant");
C.Add("Candy");

mainList.Add(new KeyValuePair<string, List<string>>("unRead", A));
mainList.Add(new KeyValuePair<string, List<string>>("unRead", B));
mainList.Add(new KeyValuePair<string, List<string>>("unRead", C));

List<string> compareList = new List<string>();
compareList.Add("Car");
compareList.Add("Boat");
compareList.Add("Dog");
List<string> resList = new List<string>();

我的第一个想法是使用 foreach 循环来遍历它,在我当前的代码中,我在 mainList 中有 32 个列表。

foreach(var item in mainList)
{
    if(item.key == "unRead")
    {
        foreach(var subItem in item.value) // evt do a List<string> temp = item.value first
        {
            foreach(var compItem in compareList)
            {
                if(compItem == subItem) resList.Add(compItem);
            }
        }
        item.remove(); // I actualy wanted to change from unRead to read, but I figured I could just remove it from the list.
    }
}

如果我使用 Threads 并在单独的线程中遍历每个列表,evt 的最大线程数量为 10 左右,这应该会更快。 所以我尝试的是这样做。

foreach(var item in mainList)
{
    Thread myThread = new Thread(() =>
    {
        if(item.key == "unRead")
        {
            foreach(var subItem in item.value) // evt do a List<string> temp = item.value first
            {
                foreach(var compItem in compareList)
                {
                    if(compItem == subItem) resList.Add(compItem);
                }
            }
            item.remove(); // I actualy wanted to change from unRead to read, but I figured I could just remove it from the list.
        }
    });
    myThread.start();
}

但这根本没有给出任何预期的输出...

那我做错了什么?

resList应该加锁,因为是在多线程修改的。您还需要等到所有线程都完成。您应该为此使用 Parallel.Foreach()

例如:

Parallel.Foreach(mainList, (item) =>
{
    if(item.key == "unRead")
    {
        foreach(var subItem in item.value) // evt do a List<string> temp = item.value first
        {
            foreach(var compItem in compareList)
            {
                if(compItem == subItem) 
                    lock(resList)
                        resList.Add(compItem);
            }
        }
    }
});

mainList.RemoveAll(item => item.key == "unRead");

这可以用一些 linq-magic 来缩短:

Parallel.Foreach(mainList, (item) =>
{
    if(item.key == "unRead")
    {
        foreach(var subItem in item.value) // evt do a List<string> temp = item.value first
        {
            if(compareList.Contains(subItem))
                lock(resList)
                    resList.Add(compItem);
        }
    }
});

没有threading/locking:

var resList = mainList.Where(item => item.key == "unRead")
                      .SelectMany(subitem => compareList.Contains(subitem))).ToList();

对于 compareList

使用 HashSet<> 而不是 List<> 会很有用

正如 Jerone van Langen 所指出的,问题是对列表的访问导致了竞争条件。您可以通过引入锁来解决这个问题,也可以使用线程安全的集合实现。后一种方法包括无锁工作的 类,这可能会给您带来更好的性能结果。例如,您可以查看 System.Collections.Concurrent.ConcurrentBag.

无论哪种方式,第一个优化肯定是对比较列表使用哈希集,至少如果您希望这些列表包含超过 10 个元素(否则,列表可能更快)。