【问题标题】:Simoultanious data insertion into a list with multithreading使用多线程同时将数据插入到列表中
【发布时间】:2018-12-17 12:26:13
【问题描述】:

我正在尝试优化一个小程序。所以这是基本的想法:

我有一个未经过滤的数据数组,我想将它传递给一个函数,该函数将调用另一个函数,两次,用于数据过滤和插入新列表。第一次调用将从原始数组中获取数据,范围为 0 => 数组长度的一半,第二次调用将执行相同操作,但范围从一半到最后一项。这样,我应该将过滤后的数据同时插入到同一个列表中。插入完成后,过滤后的列表可以传递给程序的其余部分。代码如下:

    static void Main(string[] 
        {
            // the unfiltered list
            int[] oldArray = new int[6] {1,2,3,4,5,6};
            // filtered list
            List<int> newList= new List<int>();
            // Functions is my static class
            Functions.Insert(newList, oldArray )
            Continue_Program_With_Filtered_List(newList);
            // remaining functions...
        }

这里是函数类:

public static class Functions
{
    public static void Insert(List<int> newList, int[] oldArray)
    {
        new Thread(() =>
        {
            Inserter(newList, oldArray, true);
        }).Start();
        new Thread(() =>
        {
            Inserter(newList, oldArray, false);
        }).Start();

        // I need to wait the result here of both threads
        // and make sure that every item from oldArray has been filtered
        // before I proceed to the next function in Main()
    }

    public static void Inserter(List<int> newList, int[] oldArray, bool countUp)
    {
        bool filterIsValid = false;
        int length = oldArray.Length;
        int halflen = (int)Math.Floor((decimal)length / 2);
        if (countUp)
        {
            // from half length to 0
            for (int i = 0; i < halflen; i++)
            {
                // filtering conditions here to set value of filterIsValid
                if(filterIsValid)
                    newList.Add(oldArray[i]);
            }
        }
        else
        {
            // from half length to full length
            for (int i = halflen + 1; i < length; i++)
            {
                // filtering conditions here to set value of filterIsValid
                if(filterIsValid)
                    newList.Add(oldArray[i]);
            }
        }
    }
}

所以问题是我必须等待 Function.Insert() 来完成每个线程,并在将 newList 传递给 Main() 中的下一个函数之前 传递每个项目。

我不知道如何在这样的事情上使用任务或异步方法。顺便说一下,这只是程序的概要。有什么帮助吗?

【问题讨论】:

  • .NET 已经有并发集合,例如 ConcurrentQueue 和 ConcurrentDictionary。如果你想让List 线程安全,你必须在尝试读/写时使用lock。您无法通过条件检查确保线程安全。 List的修改方法不是线程安全的
  • 另一种选择是从每个线程返回一个列表,并在线程完成工作后合并这些列表。所以你不必关心并发写入,但它期望项目顺序符合所需的顺序。
  • @PanagiotisKanavos 你说的是真的。即使有这么小的模拟数据,插入也已经很狡猾了。不知道 ConcurrentQueue 命名空间。谢谢
  • @user743414 忘了说,顺序无关紧要。该列表将被插入到字典中,然后在需要时进行搜索。或者,我将使用上面的答案,并尝试立即将其插入并发字典。

标签: c# multithreading list asynchronous synchronous


【解决方案1】:

在您的情况下,使用PLINQ 也可能是一种选择。

static void Main(string[] args)
{
    // the unfiltered list
    int[] oldArray = new int[6] { 1, 2, 3, 4, 5, 6 };
    // filtered list
    List<int> newList = oldArray.AsParallel().Where(filter).ToList();
    // remaining functions...
}

您也可以使用AsOrdered()来维护订单


回到您最初的问题,您可以执行以下操作
注意:解决方案对原始代码的更改最少,无论是否有其他可能的优化
附加说明:请记住,仍然可能存在并发问题,具体取决于您对传递给该函数的参数执行的其他操作。

public static async Task Insert(List<int> newList, int[] oldArray)
{
    ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
    var task1 = Task.Factory.StartNew(() =>
    {
        Inserter(concurrentBag, oldArray, true);
    });
    var task2 = Task.Factory.StartNew(() =>
    {
        Inserter(concurrentBag, oldArray, false);
    });
    await Task.WhenAll(task1, task2);
    newList.AddRange(concurrentBag);
}

public static void Inserter(ConcurrentBag<int> newList, int[] oldArray, bool countUp)
{
    //Same code
}

编辑:你的第二个for-loop是错误的,改成这个,否则你会丢失一个项目

for (int i = halflen; i < length; i++)

【讨论】:

  • 是的,存在一些并发问题。但至少我现在走在正确的轨道上。结合这些答案应该可以解决问题。我会在我对此进行更多测试时发布。我从这些回复中获得了很多新信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多