2014-03-27 53 views
2

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

所以我决定研究线程,并在应用程序继续正常工作时进行加载。我通过沿下方的伪码的线路做一些事来报复的基本概念与线程池的工作:

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; 
    } 
} 

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

我已经阅读了几个关于如何使用Control.Invoke(委托)设置事件和委托时使用Windows窗体的深入解释。但是我没有使用Windows窗体,也没有能够应用这些想法。我想我需要一个更通用的方法,不依赖于Control类。如果你确实做出了回应,请详细说明,也许在我的伪代码中使用一些命名。这样我就会更容易跟随。线程似乎是一个相当深刻的话题,我只是要掌握基本知识。请随时就如何改进我的问题提出建议,使其更加清晰。

+0

为什么'loadInfoObject as pv_LoadInfo'? –

+0

我的不好 - 编辑修复 – absentia

+0

如果你使用.NET FW 4.5,你可能想看看'await' /'async'。此外,您的代码不是线程安全的 - “字典”不*线程安全 - 如果您需要共享该字典,请改用'ConcurrentDictionary '。 *您使用的是什么GUI?WPF也有Invoke(请看'Dispatcher')。如果你的“GUI”是一个控制台,它会变得棘手,你需要在你的主循环中加入一个消息队列(可能是'ConcurrentQueue ')并定期检查它 - 如果它有数据,则立即出队并处理它。 – Luaan

回答

4

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

执行此操作的一种方法是使用SynchronizationContext。 这有点棘手的管理,但也有其进入它是如何工作的,以及如何可以使自己的几篇文章:

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

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

您还可以使用由后台线程写入的线程安全队列对消息进行排队,并在主线程中读取一个简单变量不足的情况。这实际上是大多数调度程序实现在底层实际上正在做的事情。

+0

噢耶。 SynchronizationContext可能就是票证。写得很好的信息也是。我将不得不花费一些时间来处理它。 – absentia