2011-05-05 79 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