2014-05-18 46 views
2

我必須做一個非常容易的任務:1)列出目錄(及其子目錄)中的所有文件,2)在多行文本框中顯示它們,然後3)在每個文件中做一些東西。我被困在2)由於2個問題,這是我有:BackgroundWorker凍結我的UI和BeginInvoke似乎去烏龜的速度

  1. Form1.cs就是我管理的UI,並開始一個BackgroundWorker 在運行Logic.cs的主要功能
  2. DependencyMapper.cs是.. 。好吧,我在那裏做文件夾/文件(在Fetch()中),並使用BeginInvoke將每行(當前文件名)填充到Form1的文本框中調用Form1函數。

說話少,代碼多。這是我的代碼的骨感,不亦樂乎工作版本:

Form1.cs

public partial class Form1 : Form 
{ 
    public DependencyMapper dep; 
    BackgroundWorker bwDep; 

    public Form1() 
    { 
     // I read here in SO to try put the BW stuff here don't know why, but hasn't helped. 
     InitializeComponent(); 

     bwDep = new BackgroundWorker(); 
     bwDep.DoWork += bwDep_DoWork; 
     bwDep.RunWorkerCompleted += bwDep_RunWorkerCompleted; 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
      bwDep.RunWorkerAsync(); 
    } 
    void bwDep_DoWork(object sender, DoWorkEventArgs e) 
    { 
     dep.Fetch(extensions); 
    } 
    public void SendBack(string msg) // To receive Fetch()s progress 
    { 
     textBox2.BeginInvoke(new Action(() => 
     { 
      textBox2.Text += msg + "\r\n"; 
      textBox2.SelectionStart = textBox2.Text.Length; 
      textBox2.ScrollToCaret(); 
     })); 
    } 
} 

DependencyMapper.cs

public class DependencyMapper 
{ 
    private Form1 form; 
    public DependencyMapper(Form1 form1) 
    { 
     this.form = form1; 
    } 
    public void Fetch() 
    { 
     DirectoryInfo folder = new DirectoryInfo(form.Texto1); 
     FileInfo[] files = folder.GetFiles("*.*", SearchOption.AllDirectories); 
     for (int i = 0; i < files.Length; i++) 
     { 
      form.SendBack(files[i].FullName); // Kind of talking back to the UI through form's reference and SendBack method which uses BeginInvoke. 
     } 
    } 
} 

因此,沒有我的應用程序的工作?是的,但兩個巨大的問題,我解決不了:

  1. 它凍結UI(WTF懶BackgroundWorker的?)。不完全是因爲文本框逐個添加每個文件,但它像是應該的,但我無法移動窗口或單擊任何按鈕。
  2. 這是veeery緩慢。肯定我做錯了什麼。我的應用程序目前以每秒10個文件的速度填充文本框。而我編碼它來查找文本的特定片段在數百個文件... OMG

PS:使用的BackgroundWorker之前,我使用線程:用戶界面沒有凍結,不過在文本框填充比例爲一樣慢。這就是爲什麼我決定冒險與BackgroundWorker,只帶來了問題#1。

謝謝。

+0

嘗試在窗體初始化之前將線程(背景)準備好與文件夾和文件的列表一起準備好。考慮如何通過表格構建或在全球範圍內提供的列表。 – ray

+1

'dep.Fetch(extensions);'什麼是dep?什麼是擴展?取參數的功能在哪裏?請張貼確切的代碼。如果適用,考慮只將最終的relult'string []'傳遞給表單。 –

+0

當您需要在後臺執行某些操作時,線程非常方便。自從您不斷與用戶界面交談之後,您實際上並未從中受益。當您完全在後臺完成繁重的操作並在完成後將結果傳遞給您的用戶界面時,纔會帶來真正的好處。 – Silvermind

回答

3

你是fire-hosing UI線程,產生的結果遠遠快於UI可以顯示它們。它可能起步相當好,但隨着時間的推移會變得越來越糟。您強制文本框重新分配內存以找到附加字符串的空間,複製原始文本並追加新文本。當文本框包含一個兆字節的文本時,這會嚴重拖動。與System.String類具有相同類型的問題。其中有StringBuilder作爲修復,TextBox沒有任何類似的東西。

通常的症狀是UI線程將開始刻錄100%核心。在某個關鍵點,它會完全緊張,而不是重新繪製UI並且對輸入沒有反應。因爲每次更新Text屬性時,都有另一個委託來調用。沒有時間去做低優先級的工作,比如繪製和清空消息隊列。調用隊列本身開始落後,收集越來越多的正在等待處理的調用請求。在極端的情況下,這會使您的程序在OutOfMemoryException中崩潰,但這需要很長時間。即使後臺工作完成後,UI線程也會在此之後繁忙很長時間,試圖減少積壓。

用戶界面通常無法使用,甚至在觸及牆壁之前。用戶只是盯着一個盲目快速的滾動文本框,它不允許實際讀取任何內容。本身使用文本框會非常不利,用戶編輯列表沒有任何意義。

顯然你需要以不同的方式做到這一點。在調用之前,至少要在StringBuilder中收集大量文件。這將使文本框不得不經常重新分配。每50毫秒更頻繁地調用一次是沒有任何意義的,這與人眼可以看到變化一樣快,而不會變成模糊。大多數執行此操作的程序只顯示示例正在迭代的文件,以便用戶對進度有一些反饋。在這種情況下,在StringBuilder中收集數據全部是完全合理的,並且在文件迭代完成之前不更新文本框。

+0

我放棄了更新用戶的進度(我仍然編碼我想要做的實際邏輯),而是將結果全部打包。但這是一個令人難以置信的迴應,給了我一個廣闊的視角,謝謝。 –