【问题标题】:multiple threads providing data to List<List<string>> to mainthread; multithreading向主线程提供数据到 List<List<string>> 的多个线程;多线程
【发布时间】:2012-09-06 20:02:19
【问题描述】:

我有多个线程创建一个public List&lt;List&lt;String&gt;&gt; listOfLists 并用数据填充它。完成所有线程后,我想使用list.AddRange() 将主线程listOfLits 中多个线程中的所有这些列表组合起来。

所以,如果我有 3 个线程并且每个线程的 listOfLists 有 10 个元素,那么主线程的 listOflists 将有 30 个元素。只需简单地添加它们,主线程对象必须具有线程插入的所有数据。

如果我将static 关键字用于listOfLists,它会完美运行。我只想摆脱static

这是一个小例子。

  1. 我创建了一个简单的 Person 对象(主线程)
  2. 主线程调用方法创建3个线程
  3. 这些线程中的每一个都调用另一个方法来用数据填充自己的listOfLists
  4. 在该方法结束时,我将自己的线程 listOfLists 添加到 static listOfLists
  5. 这就是我想要避免的。不能有任何静态。
  6. 我只想让这些线程数据在我的主线程中可用,并在主线程上继续应用程序。

编辑:它必须是 .NET 3.5

Edit2:一种解决方案似乎是在启动线程时提供 listOfLists(主线程)的引用作为参数。有没有其他方法可以解决这样的线程问题?


namespace Threading
{
    class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person("mainThread");
            p.startMultiThreading();
        }                   
 }

class Person
{
    public String name;
    public List<String> privatePet;
    public List<List<String>> listOfLists = new List<List<string>>();
    public static List<List<String>> familyPets = new List<List<string>>();

    public Person(String n) { name = n; } //constructor
    public Person() { }

    public void startMultiThreading()
    {
        List<Thread> list_threads = new List<Thread>();
        for (int i = 0; i < 3; i++)
        {
            Person multiThreadPerson = new Person("multi: " + i); //create new Person
            Thread t = new Thread(multiThreadPerson.fill_List); //create new Thread
            t.Name = multiThreadPerson.name; //name the Thread with PersonName

            list_threads.Add(t);
            list_threads[i].Start(); //new Person is calling fill_list()
        }

        for (int i = 0; i < list_threads.Count; i++)
        {
            list_threads[i].Join();
        }

        familyPets.Add(new List<string>{"this is mainthread again"});
        foreach (List<String> list in familyPets)
        {
            list.ForEach(e => Debug.WriteLine(e));
        }
    }

    public void fill_List()
    {
        privatePet = new List<string>();
        lock (familyPets)
        {
            for (int i = 0; i < 20; i++)
            {
                privatePet.Add("dog :" + Thread.CurrentThread.Name);
                privatePet.Add("cat :" + this.name);
                listOfLists.Add(privatePet);
            }
            familyPets.AddRange(listOfLists); //adding up all listOfLists to the mainthread listOfLists
        }//lock 
    }
}

【问题讨论】:

    标签: c# multithreading list concurrency .net-3.5


    【解决方案1】:

    除了ThreadStart 委托之外,您还可以使用ParameterizedThreadStart 委托创建线程,然后使用带有单个对象参数的重载Start()。您可以使用需要传递到线程的 delgate 的任何信息来创建该对象,在这种情况下,它只是您关注的列表的单个列表。

    还请注意,您的锁定范围如此之广,以至于只能让一个这样的线程执行任何操作,从而破坏了多线程。您希望他们自己处理只有他们可以处理的事情(例如,privatePet 在这种情况下,还有i,因为每个线程都有自己独立的视图。Thread.CurrentThread 是静态但线程安全的(否则将毫无意义)虽然this 不是线程安全的并且是共享的,但是在零线程写入时从字符串字段读取多个线程是安全的(您可以通过使@ 987654328@ readonly 这样您就可以 100% 自信地知道,只要查看该代码,就不会向其写入任何内容,除非它竭尽全力搞砸事情。

    public void fill_List(object targetList)
    {
        List<List<String>> familyPets = (List<List<String>>)targetList;
        List<List<String>> listOfLists = new List<List<String>>();//we'll use our own local one of these thanks, as it's just this thread's concern write now.
        privatePet = new List<string>();
        for (int i = 0; i < 20; i++)
        {
            privatePet.Add("dog :" + Thread.CurrentThread.Name);
            privatePet.Add("cat :" + this.name);
            listOfLists.Add(privatePet);
        }
        //note you don't need to lock until you are doing something
        //that hits on something that other threads might hit on.
        //otherwise you've just got each thread locked for their entire
        //duration and you might as well just do the whole thing
        //in one thread.
        lock (familyPets)
        {
            familyPets.AddRange(listOfLists); //adding up all listOfLists to the mainthread listOfLists
        } 
    }
    

    然后在调用方法中,你设置你的列表并传入start

    list_threads[i].Start(whateverTheListIsCalled);
    

    【讨论】:

    • 我的第一个想法也是,提供一个参考作为参数。 list_threads[i].Start(this.listOfLists)。在fill_List() 中,每个线程都可以将他自己的listOfList 添加到引用中。有没有其他方法可以解决这样的线程问题?
    • 抱歉,我没有得到您在评论中对问题的描述。
    【解决方案2】:

    我认为问题在于您使用同一类“Person”来填充数据和连接数据。

    Person p = new Person("mainThread");
    

    从不调用 fill_List() 并且不使用变量 privatePet 和 listOfLists。

    在不过多修改代码的情况下,一个简单的解决方案是添加一个新的构造函数:

    public Person(String n, List<List<String>> pRefFamilyPets) { name = n; familyPets = pRefFamilyPets; } //constructor
    

    并覆盖这一行,添加 familyPets 引用

    Person multiThreadPerson = new Person("multi: " + i, familyPets); //create new Person
    

    我无法遵循逻辑,但这似乎避免了使用 STATIC,因为您将引用传递给其他线程。

    【讨论】:

    • 主线程对象的目的只是为了调用一个方法来创建工作线程来填充主线程列表
    • 这就是为什么我告诉你 Person p = new Person() 应该是另一个只有调用方法和 familyPets 列表的类。更好的是,将 familyPets 和 startMultiThreading() 放在 Program 类中。
    【解决方案3】:
    Enumerable.Range(0, 3).AsParallel().WithDegreeOfParallelism(3).Select(n => {
        var list = new List<string>();
        list.Add(n + ": One");
        list.Add(n + ": Two");
        list.Add(n + ": Three");
        return list;
    }).ToList();
    

    或者合并列表:

    Enumerable.Range(0, 3).AsParallel().WithDegreeOfParallelism(3).Select(n => {
        var list = new List<string>();
        list.Add(n + ": One");
        list.Add(n + ": Two");
        list.Add(n + ": Three");
        return list;
    }).Aggregate((a, b) => a.Union(b).ToList());
    

    阅读 MSDN 上的 Tasks 和 Parallel。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-19
      相关资源
      最近更新 更多