将数据从多个线程发送回主线程的最佳方法是什么?

时间:2012-08-30 21:06:27

标签: c# multithreading

我有一个mainthread,我确实创建了另一个n-threads。每个n线程都填充List<String>。当所有线程都完成后,它们会被连接起来,我希望所有这些n线程List都在主线程的List<List<String>> BUT中。主线程应该能够在List<List<String>>上运行。每个n线程都贡献了List<String>

我有c#.NET 3.5,我想避免使用static List<List<String>>

            Thread t = new Thread(someObjectForThreading.goPopulateList);
            list_threads.Add(t);
            list_threads.last().start()

list_threads中的所有线程都会继续并填充它们的List,当它们完成时我希望有类似的东西

 //this = mainThread
 this.doSomethingWith(List<List<String>>)

编辑:嗯,没有&#34;标准概念&#34;如何解决这样的任务?许多线程在列表上运行,当所有线程都加入时,mainthread可以继续操作列表。

Edit2: List<List<String>> listOfLists不得为静态。可以是public or private。首先,我需要n个线程来操作(并锁定)listOfLists,插入它们的List,并且在完成插入列表的所有n线程之后,我将加入线程并且mainthread可以继续使用businesslogic并在{{1 }}

我想我会重新阅读http://www.albahari.com/threading/报告的某些部分

4 个答案:

答案 0 :(得分:5)

这是一个使用等待句柄(在ManualResetEvent的情况下)的简单实现,允许每个工作线程向主线程发出信号,告知它完成了它的工作。我希望这有点自我解释:

private List<List<string>> _listOfLists;

public void CreateListOfLists()
{
    var waitHandles = new List<WaitHandle>();

    foreach (int count in Enumerable.Range(1, 5))
    {
        var t = new Thread(CreateListOfStringWorker);
        var handle = new ManualResetEvent(false);
        t.Start(handle);
        waitHandles.Add(handle);
    }

    // wait for all threads to complete by calling Set()
    WaitHandle.WaitAll(waitHandles.ToArray());

    // do something with _listOfLists
    // ...
}

public void CreateListOfStringWorker(object state)
{
    var list = new List<string>();
    lock (_listOfLists)
    {
        _listOfLists.Add(list);
    }

    list.Add("foo");
    list.Add("bar");

    ((ManualResetEvent) state).Set(); // i'm done
}

注意我将每个线程的List添加到列表的主列表时只是锁定。没有必要为每个添加锁定主列表,因为每个线程都有自己的List。有意义吗?

修改

使用等待句柄的目的是在处理列表列表之前等待每个线程完成。如果您不等待,那么当工作者仍在向其添加字符串时,您可能会尝试枚举其中一个List实例。这将导致抛出InvalidOperationException,并且您的线程将会死亡。您无法枚举集合并同时对其进行修改。

答案 1 :(得分:1)

而不是让static List<List<String>>创建一个本地List<List<String>>并将其传递给Object,线程将运行。当然,您需要将List包装在同步包装器中,因为它是由多个线程访问的。

List<List<String>> list = ArrayList.synchronized(new ArrayList<List<String>>());

// later
SomeObject o = new SomeObjectForThreading(list);
Thread t = new Thread(o.goPopulateList);
list_threads.Add(t);
list_threads.last().start();

// even later
this.doSomethingWith(list);

o.goPopulateList,你可能有

List<String> temp = new ArrayList<String>();
temp.add(random text);
temp.add(other random text);
this.list.add(temp); // this.list was passed in at construct time

答案 2 :(得分:1)

我会为每个线程提供一个回调方法,该方法更新主线程中的列表,并使用lock语句进行保护。

编辑:

class Program
{
    static List<string> listOfStuff = new List<string>();

    static void Main(string[] args)
    {
        List<Thread> threads = new List<Thread>();
        for (int i = 0; i < 20; i++)
        {
            var thread = new Thread(() => { new Worker(new AppendToListDelegate(AppendToList)).DoWork(); });
            thread.IsBackground = true;
            threads.Add(thread);
        }
        threads.ForEach(n => n.Start());
        threads.ForEach(n => n.Join());
        Console.WriteLine("Count: " + listOfStuff.Count());
        Console.ReadLine();
    }

    static void AppendToList(string arg)
    {
        lock (listOfStuff)
        {
            listOfStuff.Add(arg);
        }
    }
}

public delegate void AppendToListDelegate(string arg);
class Worker
{
    AppendToListDelegate Appender;

    public Worker(AppendToListDelegate appenderArg)
    {
        Appender = appenderArg;
    }

    public void DoWork()
    {
        for (int j = 0; j < 10000; j++)
        {
            Appender(Thread.CurrentThread.ManagedThreadId.ToString() + "." + j.ToString());
        }
    }
}

答案 3 :(得分:0)

private void someObjectForThreading.goPopulateList()
{
    Do threaded stuff...
    populate threaded list..

    All done..
    for (List list in myThreadedList)
    {
        myMainList.Add(list);
    }
}