2011-05-05 122 views
2

在我的应用程序的图像预加载缓存,还有通过它,用户可以一步图像列表。图像加载速度很慢,因此为了改善用户体验,我希望在背景中预加载一些图像(例如,列表中的图像在当前选定的图像之后)。C# - 如何实现与线程

我从来没有真正使用的线程在C#,所以我要寻找某种“最佳实践”的建议如何实现以下行为:

public Image LoadCachedImage(string path) 
{ 
    // check if the cache (being operated in the background) 
    // has preloaded the image 
    Image result = TryGetFromCache(path); 

    if (result == null) { result = LoadSynchronously(path); } 

    // somehow get a list of images that should be preloaded, 
    // e.g. the successors in the list 
    string[] candidates = GetCandidates(path); 

    // trigger loading of "candidates" in the background, so they will 
    // be in the cache when queried later 
    EnqueueForPreloading(candidates); 

    return result; 
} 

我相信,一个后台线程应该是监控队列,并连续处理通过EnqueueForPreloading()发布的元素。我想知道如何实现后台工作线程的这个“主循环”(或者也许有更好的方法来做到这一点?)

回答

0

如果你真的需要考生的顺序处理,你可以做一个如下:

  1. 创建一个具有AutoResetEvent的消息队列数据结构。该类应该生成一个等待事件的线程,然后处理队列中的所有内容。该类的Add或Enqueue应将其添加到队列中,然后设置事件。这将释放处理队列中项目的线程。

  2. 创建类启动一个STA线程,创建一个System.Windows.Forms.Control的,然后进入Application.Run()。每次你想异步处理一个图像时,调用Control.BeginInvoke(...),并且STA线程将在其消息队列中选取它。

有可能有其他的选择,但这两个将是我会尝试。

如果你实际上并不需要顺序处理,可以考虑使用ThreadPool.QueueUserWorkItem(...)。如果有空闲的线程池,它将使用它们,否则它将排队项目。但是您不能保证处理顺序,并且可能会同时处理几个。

这里有一个消息队列的(缺陷)例如:

class MyBackgroundQueue<T> 
{ 
    private Queue<T> _queue = new Queue<T>(); 
    private System.Threading.AutoResetEvent _event = new System.Threading.AutoResetEvent(false); 
    private System.Threading.Thread _thread; 

    public void Start() 
    { 
     _thread = new System.Threading.Thread(new System.Threading.ThreadStart(ProcessQueueWorker)); 
     _thread.Start(); 
    } 

    public class ItemEventArgs : EventArgs 
    { public T Item { get; set; } } 

    public event EventHandler<ItemEventArgs> ProcessItem; 

    private void ProcessQueueWorker() 
    { 
     while (true) 
     { 
      _event.WaitOne(); 
      while (_queue.Count > 0) 
       ProcessItem(this, new ItemEventArgs { Item = _queue.Dequeue() }); 
     } 
    } 

    public void Enqueue(T item) 
    { 
     _queue.Enqueue(item); 
     _event.Set(); 
    } 
} 

一个缺陷在这里,当然是_queue没有被锁定,所以你会遇到竞争条件。但是我会留给你解决这个问题(例如使用2队列交换方法)。另外,while(true)永远不会中断,但我希望样本可以满足您的目的。

0

这就是我所说的作弊缓存。操作系统已经为您缓存文件,但您必须先访问它们。所以你可以做的只是加载文件,但不保存对它们的引用。

你可以做到这一点没有多线程每本身,并没有在列表保存图像。只需创建一个方法委托并为每个要在后台加载的文件调用。

例如,预加载所有JPEG图像中的一个目录。

Action<string> d = (string file) => { System.Drawing.Image.FromFile(file); }; 
foreach(string file in dir.GetFiles("*.jpg")) 
    d.BeginInvoke(file); 

BeginInvoke()是一个多线程的方法,这个循环会非常快,但每个文件都会加载到不同的线程上。或者你可以稍微改变一下,把循环放在代表中,也就是说。

public void PreCache(List<string> files) 
{ 
    foreach(string file in files) 
     System.Drawing.Image.FromFile(file); 
} 

然后在你的代码

Action<List<string>> d = PreCache; 
d.BeginInvoke(theList); 

然后所有的加载上只有一个工作线程来完成。

+0

BeginInvoke不会对“在不同的线程上”做任何事情。相反,它让你在UI线程上专门做一些事情。它基本上说“下一次UI线程不忙于做其他事情时,在UI线程上调用此方法”。当您的调用方法在UI线程上执行时,UI线程无法做其他事情 - 它看起来没有反应。所以......你的第一个建议可以正常工作,但第二个建议不会。 – 2011-05-06 03:22:44