2015-01-05 17 views
0

Я пытаюсь выполнить легкую задачу в другом backgroundthread, поэтому пользовательский интерфейс не блокируется, но он по-прежнему блокируется. Я что-то забыл?Блоки рабочего стола UI

public partial class backgroundWorkerForm : Form 
{ 
    public backgroundWorkerForm() 
    { 
     InitializeComponent(); 
    } 

    private void doWorkButton_Click(object sender, EventArgs e) 
    { 
     if (backgroundWorker.IsBusy != true) 
     { 
      // Start the asynchronous operation. 
      backgroundWorker.RunWorkerAsync(); 
     } 
    } 

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     //BackgroundWorker worker = sender as BackgroundWorker; 
     if (textBoxOutput.InvokeRequired) 
     { 
      textBoxOutput.Invoke(new MethodInvoker(delegate 
      { 
       for (int i = 0; i < 10000; i++) 
       { 
        textBoxOutput.AppendText(i + Environment.NewLine); 
       } 
      })); 
     } 
    } 
} 

В то время как TextBox заполняется, интерфейс заблокирован:

enter image description here

+1

Не удивительно, у вас есть цикл, который делает работу внутри Invoke (который вызывается на потоке пользовательского интерфейса) – Icepickle

+3

Единственное ваш фон работник делает отправка весь цикл в потоке пользовательского интерфейса выполнить его там. –

+0

'textBoxOutput.Invoke' будет выполнять делегат синхронно в потоке пользовательского интерфейса и, таким образом, блокирует его. Непонятно, чего вы пытаетесь достичь, но, по-видимому, вам вообще не нужен «BackgroundWorker». –

ответ

2

Ваш код вызова должен находиться вне цикла. Все в вызываемом кодовом блоке будет выполняться в потоке пользовательского интерфейса, тем самым блокируя его.

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     //BackgroundWorker worker = sender as BackgroundWorker; 
     for (int i = 0; i < 10000; i++) 
     { 
      // do long-running task 

      //if (textBoxOutput.InvokeRequired) 
      //{ 
       textBoxOutput.Invoke(new MethodInvoker(delegate 
       { 
        textBoxOutput.AppendText(i + Environment.NewLine); 
       })); 
      //} 
     } 
    } 
+1

Не знаете, почему это было поддержано. Этот код ничего не улучшит, скорее это еще хуже. -1 –

+1

@ sriram-sakthivel: вы должны попробовать код. Он больше не блокирует пользовательский интерфейс. –

+1

Из интереса, почему вы вызываете 'InvokeRequired' внутри цикла? Как вы думаете, что изменится между каждой итерацией цикла? –

1

проще всего было бы сделать полностью создать свой выходной текст, а затем вставьте полный вывод в TextBox, то вам нужно только один вызов

protected delegate void SetTextDelegate(TextBox tb, string Text); 

protected void SetText(TextBox tb, string Text) 
{ 
    if (tb.InvokeRequired) { 
     tb.Invoke(new SetTextDelegate(SetText), tb, Text); 
     return; 
    } 
    tb.Text = Text; 
} 

, а затем внутри вашего DoWork

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    StringBuilder sb = new StringBuilder(); 
    //BackgroundWorker worker = sender as BackgroundWorker; 
    for (int i = 0; i < 10000; i++) 
    { 
     sb.AppendLine(i.ToString()); 
    } 
    SetText(textBoxOutput, sb.ToString()); 
} 
+2

Что это изменяет и как это отвечает на вопрос? –

+0

@SriramSakthivel, если вы поместите 'Thread.Sleep (100);' в мой код после 'AppendText', пользовательский интерфейс будет заблокирован, и мне придется подождать, пока не будут добавлены все 10000 номеров, но с этим кодом он не будет block – eMi

+0

Попробуйте запустить код и посмотреть, блокирует ли пользовательский интерфейс. Этот код делает именно то, что сделал OP довольно сложным образом. –

2

Ваше приложение хочет повторно отправлять обновления из фонового потока в пользовательский интерфейс. Для этого есть встроенный механизм: событие ProgressChanged для рабочего фона. A ReportProgress вызов запускается в фоновом режиме, но выполняется в потоке пользовательского интерфейса.

I изменение один вещь, однако. Производительность может ухудшиться при слишком большом количестве вызовов с перекрестными потоками. Таким образом, вместо того, чтобы посылать обновления каждую итерацию, я вместо этого будет партия их в 100.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     const int maxIterations = 10000; 
     var progressLimit = 100; 
     var staging = new List<int>(); 
     for (int i = 0; i < maxIterations; i++) 
     { 
      staging.Add(i); 
      if (staging.Count % progressLimit == 0) 
      { 
       // Only send a COPY of the staging list because we 
       // may continue to modify staging inside this loop. 
       // There are many ways to do this. Below is just one way. 
       backgroundWorker1.ReportProgress(staging.Count, staging.ToArray()); 
       staging.Clear(); 
      } 
     } 
     // Flush last bit in staging. 
     if (staging.Count > 0) 
     { 
      // We are done with staging here so we can pass it as is. 
      backgroundWorker1.ReportProgress(staging.Count, staging); 
     } 
    } 

    // The ProgressChanged event is triggered in the background thread 
    // but actually executes in the UI thread. 
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     if (e.ProgressPercentage == 0) return; 
     // We don't care if an array or a list was passed. 
     var updatedIndices = e.UserState as IEnumerable<int>; 
     var sb = new StringBuilder(); 
     foreach (var index in updatedIndices) 
     { 
      sb.Append(index.ToString() + Environment.NewLine); 
     } 
     textBoxOutput.Text += sb.ToString(); 
    } 

EDIT:

Для этого необходимо установить WorkerReportsProgress свойство фон работника к истине.

Не важно, чтобы вы проходили подсчет с помощью вызова ReportProgress. Я делаю это просто, чтобы иметь что-то и быстро проверить, могу ли я вернуться.

Следует помнить о том, сколько событий вызывается и ставится в очередь. У вашего оригинального приложения было 10 000 вызовов перекрестных потоков и 10 000 измененных текстовых событий для textBoxOutput. В моем примере используется 100 вызовов с перекрестными потоками, так как я использую размер страницы 100. Я мог бы сгенерировать 10 000 измененных текстовых событий для текстового поля, но вместо этого использовать объект StringBuilder для хранения полной страницы изменений и затем обновлять текстовое поле один раз для этого стр. Таким образом, текстовое поле содержит только 100 событий обновления.

EDIT 2

ли не нуждается ваше приложение подкачки не главная проблема. Самый большой урон должен заключаться в том, что фоновой работник действительно должен использовать ReportProgress при попытке передать информацию обратно в пользовательский интерфейс. См. Это MSDN Link. Особо следует отметить следующее:

Вы должны быть осторожны, чтобы не манипулировать каких-либо объектов пользовательского интерфейса в обработчика событий DoWork. Вместо этого обменивайтесь с пользовательским интерфейсом событиями ProgressChanged и RunWorkerCompleted.

+0

, это кажется прекрасным, для этой конкретной тестовой программы, которую я опубликовал, но в моем реальном приложении я не могу использовать этот подход, поскольку я не знаю, сколько будет событий обновления. – eMi

+0

Использование моего подхода (1) фоновый рабочий не блокирует пользовательский интерфейс и (2) минимизирует вызовы перекрестных потоков, а также текстовые сообщения, вызванные TextChanged, особенно в тех случаях, когда вы не знаете, сколько будет происходить обновление событий. Как это не полезно? –

+0

У меня нет (и не может быть) переменных: 'maxIterations',' progressLimit', и мне не нужно сообщать о каком-либо прогрессе. Возможно, это полезно для этой части кода, которую я опубликовал, но я не могу адаптировать ваш подход в своем приложении. В любом случае, я дам вам +1 за ваши усилия, спасибо – eMi

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