2010-10-08 5 views
0

Это мой сценарий: На форме у меня есть список directcotories, Button и control для отображения многострочного текста. В цикле я пытаюсь найти все файлы в каждом каталоге и удалить их. Когда файл удален, я хочу добавить текст в многострочный элемент управления. Моя проблема заключается в том, что при добавлении текста я ничего не могу сделать. Форма заблокирована, и если я попробую сделать anytching, она просто перестает отвечать. Файлы удаляются с помощью BackgroundWorkerФоновая работа и блокировка основной формы

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
//this is datatable with directories and other info 
     MainDataset.CZYSZCZENIEDataTable CZYSZCZENIE = e.Argument as MainDataset.CZYSZCZENIEDataTable; 
     CzyscPliki(CZYSZCZENIE, ReportProgress); 
    } 

private void CzyscPliki(MainDataset.CZYSZCZENIEDataTable CZYSZCZENIE, ReportProgressDel del) 
    { 
     DirectoryInfo dir = null; 
     FileInfo[] files = null; 
     bool subfolder = false; 
     string katalog = ""; 
     string maska = ""; 
     string[] maski = null; 
     long total=0; 

     string dirS; 
     string fileS; 
     long fileLen; 
//foreach directory to delete 
     foreach (DataRow r in CZYSZCZENIE.Rows) 
     { 
//CanRead - check if row is not deleted or detached 
//r["CZYSC"].AsBool() - check if directory should be cleared 
      if (r.CanRead() && r["CZYSC"].AsBool()) 
      { 
       subfolder = r["PODKATALOGI"].AsBool(); 
       katalog = r["KATALOG"].AsString().TrimEnd('\\'); 
       maska = r["MASKA"].AsString(); 
       if (maska.IsEmpty()) 
        maska = "*"; 
       maski = maska.Split(';'); 
       dir = new DirectoryInfo(katalog); 
       if (dir.Exists) 
       { 
        foreach (string s in maski) 
        { 
         files = dir.GetFiles(s, (subfolder ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)); 
         dir.GetFiles(); 
         foreach (FileInfo f in files) 
         { 
          dirS = f.Directory.FullName; 
          fileS = f.Name; 
          fileLen = f.Length; 
          try 
          { 
           f.Delete(); 
           total += fileLen; 
           if (del != null) 
//here is problem: del - delegate to report state 
//when it is called it blocks form 
            del(dirS, fileS, fileLen, total); 

          } 
          catch (Exception ex) 
          { } 
         } 
        } 
       } 
      } 
     } 
    } 
//this is the delegate that appends text in multiline control 
//memoEdit1 is the control 
//ceReportProgress.Checked - check if report should be added 
private void ReportProgress(string directory, string file, long size, long totalSize) 
    { 
     if (memoEdit1.InvokeRequired) 
     { 
      memoEdit1.BeginInvoke(new Action<string, string, long, long>(ReportProgress), directory, file, size, totalSize); 
     } 
     else 
     { 
      if (ceReportProgress.Checked) 
      { 
       if (file.IsEmpty()) 
        memoEdit1.AppendText("\r\nCzyszczenie katalogu " + directory); 
       else 
       { 
        memoEdit1.AppendText(file); 
        if (size > 0) 
        { 
         if (size > 1048576) 
         { 
          decimal d = size/1048576; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("\tWielkość : " + d.AsString() + " megabajtów", false); 
         } 
         else if (size > 1024) 
         { 
          decimal d = (decimal)size/(decimal)1024; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("\tWielkość : " + d.AsString() + " kilobajtów", false); 
         } 
         else 
          memoEdit1.AppendText("\tWielkość : " + size.AsString() + " bajtów", false); 
        } 
        if (totalSize > 0) 
        { 
         if (totalSize > 1073741824) 
         { 
          decimal d = (decimal)totalSize/(decimal)1073741824; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " gigabajtów"); 
         } 
         else if (totalSize > 1048576) 
         { 
          decimal d = (decimal)totalSize/(decimal)1048576; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " megabajtów"); 
         } 
         else if (totalSize > 1024) 
         { 
          decimal d = (decimal)totalSize/(decimal)1024; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " kilobajtów"); 
         } 
         else 
          memoEdit1.AppendText("Zwolniono dotychczas : " + totalSize.AsString() + " bajtów"); 
        } 
       } 
//scroll to the end of control 
       memoEdit1.ScrollToEnd(); 
      } 
     } 
    } 

Как я могу улучшить это, чтобы сделать его не блокирует форму?

+0

Я не вижу, где вы создаете своего рабочего. Вы называете 'RunWorkerAsync();'? –

+0

Я вызываю backgroundWorker1.RunWorkerAsync (GetRowsToDelete()); когда пользователь нажимает кнопку GetRowsToDelete() возвращает таблицу с удаляемыми каталогами. BackgroundWorker отлично работает и удаляет файлы, но показывает информацию блокирует мою форму. – Pawel

+0

Перерыв в отладчик и посмотреть, где блокируются потоки.Есть ли обработчик, подписанный на событие TextChanged memoEdit1? – Henrik

ответ

2

Вы слишком часто вызываете ReportProgress. Делайте это более чем 1000 раз в секунду, и поток пользовательского интерфейса заполняется запросами, которые он не может выполнить. Он не будет выполнять свои обычные обязанности, включая окраску элементов управления и реакцию на мышь и клавиатуру. Он выглядит замороженным. Это ухудшается, когда код обновления пользовательского интерфейса становится дороже, обновляя текст в TextBox, когда в нем уже много текста, он может стать довольно медленным.

Диагностика по-прежнему наблюдается, когда пользовательский интерфейс заморожен некоторое время после того, как BGW перестает работать, работая над освобождением отставания в очереди запросов, а затем внезапно возвращается обратно, когда очередь окончательно опустеет.

Вам необходимо активировать скорость, с которой вы звоните BeginInvoke(). Никогда не имеет смысла называть это чаще, чем раз каждые 50 миллисекунд, человек не может воспринимать разницу выше этого. Соберите информацию в списке <>, чтобы вы могли BeginInvoke() намного реже. Это все еще не является полной гарантией, если ваш работник может производить результаты быстрее, чем поток пользовательского интерфейса может не отставать. В этом случае замедление работы рабочего было бы проблемой. Легко использовать Invoke вместо BeginInvoke.

+0

Отличный момент, Ханс! Я пропустил это :) +1 – Nayan

+0

Спасибо :) Я добавил Thread.Sleep (10) перед вызовом backgroundWorker1.ReportProgress, и он работает очень хорошо. Я также добавлю результаты в список <>, так что backgroundWorker1.ReportProgress будет называться менее часто – Pawel

0

Если этот рабочий работает асинхронно, вы можете получить форму, которая вам ответит.

Кроме того, проблемы:

  1. Вы работаете петлю в другую функцию - он делает операцию не-reponsive.

  2. Вы даже не проверяя, если пользователь хочет отменить (только точку я хотел сделать) - Ручка DoWorkEventArgs «s Cancel свойства внутри цикла foreach.

Переместить код функции CzyscPliki «s в backgroundWorker1_DoWork (это все равно слишком мало).

EDIT:

Если вы не хотите, чтобы переместить код в DoWork обработчик событий, то лучше использовать Thread для большего контроля. Я не эксперт в этом, но вы найдете много кода о том, как это реализовать.

+0

Но CzyscPliki уже вызывается из backgroundWorker1_DoWork, поэтому он уже выполняется в другом потоке. – kevev22

+0

Это не главное. Проблема заключается в циклизации во внешней функции, а не в том, что внешняя функция не вызывается. – Nayan

+0

Функция CzyscPliki используется также где-то в другом месте, и даже если я перемещаю его код в backgroundWorker1_DoWork, он все равно не работает. Можете ли вы дать мне пример кода, который будет использовать Фоновый работник? Допустим, что в while (true) работник цикла меняет форму текста на текущее время. Это из-за отказа сторонников – Pawel

Смежные вопросы