Threadpool - 如何从工作线程调用主线程中的方法(带params)

时间:2014-03-27 08:07:48

标签: c# multithreading threadpool

我正在尝试线程化应用程序。该应用程序使用大型数据集,该数据集被分成可存储在磁盘上的可管理块,因此整个数据集永远不必一次驻留在内存中。相反,可以根据需要逐个加载数据的子集。这些块以前是在主线程中一个接一个地加载的。当然,这将有效地暂停所有GUI和其他操作,直到数据完全加载。

所以我决定研究线程,并在应用程序继续正常运行时进行加载。通过执行以下伪代码的操作,我能够使用ThreadPool获得基本概念:

public class MyApp
{
    List<int> listOfIndiciesToBeLoaded; //This list gets updated based on user input
    Dictionary<int,Stuff> loadedStuff = new Dictionary<int,Stuff>();

    //The main thread queues items to be loaded by the ThreadPool
    void QueueUpLoads()
    {
        foreach(int index in listOfIndiciesToBeLoaded)
        {
            if(!loadedStuff.ContainsKey(index))
                loadedStuff.Add(index,new Stuff());

            LoadInfo loadInfo = new LoadInfo(index); 
            ThreadPool.QueueUserWorkItem(LoadStuff, loadInfo);
        }
    }

    //LoadStuff is called from the worker threads
    public void LoadStuff(System.Object loadInfoObject)
    {
        LoadInfo loadInfo = loadInfoObject as LoadInfo;
        int index = loadInfo.index;

        int[] loadedValues = LoadValuesAtIndex(index); /* here I do my loading and ...*/

        //Then I put the loaded data in the corresponding entry in the dictionary
        loadedStuff[index].values = loadedValues;
        //Now it is accessible from the main thread and it is flagged as loaded
        loadedStuff[index].loaded = true;
    }   
}

public class Stuff
{
    //As an example lets say the data being loaded is an array of ints
    int[] values;
    bool loaded = false;
}

//a class derived from System.Object to be passed via ThreadPool.QueueUserWorkItem
public class LoadInfo : System.Object
{
    public int index;

    public LoadInfo(int index)
    {
        this.index = index;
    }
}

与我在过去几天尝试学习这些东西时遇到的非常复杂的例子相比,这是非常原始的。当然,它会同时加载数据并将其填充到可从主线程访问的字典中,但它也给我留下了一个至关重要的问题。我需要在加载项目时通知主线程以及它是哪个项目以便可以处理和显示新数据。理想情况下,我希望每个完成的加载调用主线程上的函数,并为其提供索引和新加载的数据作为参数。我知道我不能从多个其他同时运行的线程调用主线程上的函数。它们必须以某种方式排队,以便主线程在没有做其他事情时运行它们。但这是我目前对线程通信的理解下降的地方。

我已经阅读了一些有关如何在使用Windows窗体时使用Control.Invoke(委托)设置事件和委托的深入解释。但我没有使用Windows Forms,并且无法应用这些想法。我想我需要一种更通用的方法,它不依赖于Control类。如果您做出回应,请详细说明并使用我的伪代码中的一些命名。这样我就会更容易跟随。线程似乎是一个非常深刻的话题,我只是想掌握基础知识。另外,请随时就如何改进我的问题提出建议,以便更清楚。

1 个答案:

答案 0 :(得分:4)

如果您没有使用某种调度程序或GUI线程(如WPF或WinForms)的GUI框架,那么您必须手动执行此操作。

执行此操作的一种方法是使用SynchronizationContext。 管理起来有些棘手,但有一些文章介绍了它的工作原理以及如何制作自己的文章:

http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I http://www.codeproject.com/Articles/32113/Understanding-SynchronizationContext-Part-II

但是我也会考虑使用一个'DictionaryChanged'布尔值,它由你的'主线程'(当它是空闲时)定期检查,以指示字典已被更改。然后可以在主线程上重置该标志以指示已经处理了该标志。请记住,你需要在那里做一些锁定。

您还可以使用线程安全队列对消息进行排队,该队列由后台线程编写,如果简单变量不足,则从主线程读取。这基本上是大多数调度员实施的内容。