Форум программистов, компьютерный форум, киберфорум
C# Windows Forms
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.80/75: Рейтинг темы: голосов - 75, средняя оценка - 4.80
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
1
.NET 4.x

Зависание программы при выполнении длительного цикла

26.01.2015, 16:26. Показов 15001. Ответов 27
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
В моей программе выполняется долго, очень долго цикл, так как много данных обрабатывается.
Программа работает и отрабатывает цикл нормально.
Но на время выполнения цикла, программа как бы зависает.
Как это происходит.
Если переключиться на другую программу, а потом вернуться, то в тайтле программы добавляется текст "Не отвечает" и при клике на программу она покрывается белым фоном.
Такое состояние до момента завершения цикла, после чего программа возвращается в нормальное состояние и в цикле все выполнено правильно.

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

Пробовал также добавлять в каждый шаг итерации цикла Application.DoEvents();, но также не помогает.

Какие еще есть варианты?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
26.01.2015, 16:26
Ответы с готовыми решениями:

Зависание формы при выполнении цикла
Есть бот который выполняет get запросы каждые 30 секунд. Запрос находится в исключении, а...

Постепенное выполнение программы/зависание программы при выполнении
Собственно есть вопрос, возможно очень даже глупый, но все же спрошу: почему при выполнении...

При выполнении кода, полное зависание программы
Private Sub Кнопка2_Click() Dim x As Integer, i As Integer Dim a As Double, W As Double a =...

Чувствительность программы во время длительного цикла
И снова вынужден обратиться к вам за помощью. Есть такая ситуация: for (int i=0;i<индекс;i++){...

27
1245 / 1055 / 293
Регистрация: 07.03.2012
Сообщений: 3,245
26.01.2015, 16:30 2
Цитата Сообщение от Vitukr Посмотреть сообщение
через BackgroundWorker, но ничего не меняется.
значит что то не так. Единственный способ решения - вывод тяжёлых операций в отдельные потоки
0
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
26.01.2015, 16:36  [ТС] 3
Там много параметров передается и по ходу цикла идет обновление значений, скажем в таблицах. Может это влияет? Параметры вроде я корректно передаю, в объекте аргумента. Там же и таблицы передаются. То есть не нарушается принадлежность потокам, вроде.
0
1245 / 1055 / 293
Регистрация: 07.03.2012
Сообщений: 3,245
26.01.2015, 18:00 4
без кода мы можем бесконечно долго рассуждать, что у вас там не так
0
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
26.01.2015, 18:18  [ТС] 5
Вот здесь запускается поток
C#
1
2
3
4
5
6
7
8
9
10
11
12
Arguments arguments = new Arguments();
                    arguments.checkBox_same = checkBox_same;
                    arguments.listBox_primary_key = listBox_primary_key;
                    arguments.listBox_primary_key_excel = listBox_primary_key_excel;
                    arguments.listBox_fields = listBox_fields;
                    arguments.listBox_fields_excel = listBox_fields_excel;
                    arguments.dataGridView_excel_file = dataGridView_excel_file;
                    arguments.dataGridView_currenttable = dataGridView_currenttable;
                    arguments.updatedRows = updatedRows;
                    arguments.newPriceColor = newPriceColor;
 
                    backgroundWorker1.RunWorkerAsync(arguments);
Здесь выполняется
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            Arguments arguments = e.Argument as Arguments;
            update_dataset_background(worker, e, arguments.checkBox_same, ref arguments.listBox_primary_key, ref arguments.listBox_primary_key_excel, ref arguments.listBox_fields, ref arguments.listBox_fields_excel, ref arguments.dataGridView_excel_file, ref arguments.dataGridView_currenttable, ref arguments.updatedRows, ref arguments.newPriceColor);
            e.Result = arguments;
        }
 
public void update_dataset_background(BackgroundWorker worker, DoWorkEventArgs e, CheckBox checkBox_same, ref ListBox listBox_primary_key, ref ListBox listBox_primary_key_excel, ref ListBox listBox_fields, ref ListBox listBox_fields_excel, ref DataGridView dataGridView_excel_file, ref DataGridView dataGridView_currenttable, ref uint updatedRows, ref Color newPriceColor)
        {
            try
            {
                if (dataGridView_currenttable.DataMember == "TableCurrent")
                {
                    if (checkBox_same.Checked)
                    {
                        update_dataset_same(worker, ref listBox_primary_key, ref listBox_primary_key_excel, ref listBox_fields, ref listBox_fields_excel, ref dataGridView_excel_file, ref dataGridView_currenttable, ref updatedRows, ref newPriceColor);
                    }
                    else
                    {
                        update_dataset(worker, ref listBox_primary_key, ref listBox_primary_key_excel, ref listBox_fields, ref listBox_fields_excel, ref dataGridView_excel_file, ref dataGridView_currenttable, ref updatedRows, ref newPriceColor);
                    }
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message);
            }
        }
 
private void update_dataset(BackgroundWorker worker, ref ListBox listBox_primary_key, ref ListBox listBox_primary_key_excel, ref ListBox listBox_fields, ref ListBox listBox_fields_excel, ref DataGridView dataGridView_excel_file, ref DataGridView dataGridView_currenttable, ref uint updatedRows, ref Color newPriceColor)
        {
            try
            {
                updatedRows = 0;
                if (listBox_primary_key.SelectedItems.Count > 0 && listBox_primary_key_excel.SelectedItems.Count > 0)
                {
                    if (listBox_primary_key.SelectedItems.Count == listBox_primary_key_excel.SelectedItems.Count)
                    {
                        if (listBox_fields.SelectedItems.Count > 0 && listBox_fields_excel.SelectedItems.Count > 0)
                        {
                            if (listBox_fields.SelectedItems.Count == listBox_fields_excel.SelectedItems.Count)
                            {
                                // ProgressBar
                                //this.startProgressBar(this.dataGridView_excel_file.RowCount);
                                for (int i = 0; i < dataGridView_excel_file.RowCount; i++)
                                {
                                    // Get primary keys
                                    List<string> excel_keys = new List<string>();
                                    for (int ii = 0; ii < listBox_primary_key_excel.SelectedItems.Count; ii++)
                                    {
                                        if (string.IsNullOrEmpty(dataGridView_excel_file.Rows[i].Cells[listBox_primary_key_excel.SelectedItems[ii].ToString()].Value.ToString().Trim()))
                                        {
                                            excel_keys.Clear();
                                            continue;
                                        }
                                        excel_keys.Add(dataGridView_excel_file.Rows[i].Cells[listBox_primary_key_excel.SelectedItems[ii].ToString()].Value.ToString().Trim());
                                        //Application.DoEvents();
                                    }
                                    if (excel_keys.Count == 0)
                                    {
                                        continue;
                                    }
                                    for (int j = 0; j < dataGridView_currenttable.SelectedRows.Count; j++)
                                    {
                                        if (dataGridView_currenttable.SelectedRows[j].DefaultCellStyle.BackColor != newPriceColor)
                                        {
                                            // Get primary keys
                                            List<string> table_keys = new List<string>();
                                            for (int jj = 0; jj < listBox_primary_key.SelectedItems.Count; jj++)
                                            {
                                                if (string.IsNullOrEmpty(dataGridView_currenttable.SelectedRows[j].Cells[listBox_primary_key.SelectedItems[jj].ToString()].Value.ToString().Trim()))
                                                {
                                                    table_keys.Clear();
                                                    continue;
                                                }
                                                table_keys.Add(dataGridView_currenttable.SelectedRows[j].Cells[listBox_primary_key.SelectedItems[jj].ToString()].Value.ToString().Trim());
                                                //Application.DoEvents();
                                            }
                                            if (table_keys.Count == 0)
                                            {
                                                continue;
                                            }
 
                                            // Exists
                                            if (excel_keys.Intersect(table_keys).Count<string>() == excel_keys.Count)
                                            {
                                                bool updated = false;
                                                for (int k = 0; k < listBox_fields.SelectedItems.Count; k++)
                                                {
                                                    Type valueType = dataGridView_currenttable.Columns[listBox_fields.SelectedItems[k].ToString()].ValueType;
                                                    object valueFrom = dataGridView_excel_file.Rows[i].Cells[listBox_fields_excel.SelectedItems[k].ToString()].Value;
                                                    if (correctValue(valueType, ref valueFrom))
                                                    {
                                                        dataGridView_currenttable.SelectedRows[j].Cells[listBox_fields.SelectedItems[k].ToString()].Value = valueFrom;
                                                        //dataGridView_currenttable.BeginInvoke(dataGridView_currenttable.Update());
                                                        updated = true;
                                                    }
                                                }
                                                if (updated)
                                                {
                                                    dataGridView_currenttable.SelectedRows[j].DefaultCellStyle.BackColor = newPriceColor;
                                                    updatedRows++;
                                                }
                                                break;
                                            }
                                        }
                                        //Application.DoEvents();
                                    }
                                    //this.progressBar.PerformStep();
                                    //int percentComplete = (int)(i / (float)(this.dataGridView_excel_file.RowCount * 100.0));
                                    worker.ReportProgress(i);
                                }
                            }
                        }
                    }
                }
                //this.progressBar.Visible = false;
                dataGridView_currenttable.Select();
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message);
            }
        }
И по завершению цикла
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //throw new NotImplementedException();
            this.progressBar.Visible = false;
            MessageBox.Show("Обновилось " + this.updatedRows.ToString() + " строк");
            Arguments arguments = e.Result as Arguments;
            checkBox_same = arguments.checkBox_same;
            listBox_primary_key = arguments.listBox_primary_key;
            listBox_primary_key_excel = arguments.listBox_primary_key_excel;
            listBox_fields = arguments.listBox_fields;
            listBox_fields_excel = arguments.listBox_fields_excel;
            dataGridView_excel_file = arguments.dataGridView_excel_file;
            dataGridView_currenttable = arguments.dataGridView_currenttable;
            updatedRows = arguments.updatedRows;
            newPriceColor = arguments.newPriceColor;
        }
0
1245 / 1055 / 293
Регистрация: 07.03.2012
Сообщений: 3,245
26.01.2015, 20:19 6
Цитата Сообщение от Vitukr Посмотреть сообщение
Здесь выполняется
по-моему, смысла от потока в вашем коде никакого нет. Он везде обращается к контролам формы, изменяет их, всё это выполняется в основном потоке программы, потому и тормозит. Поток должен обрабатывать какие то данные, возвращать что-то. После чего эти данные должен использовать основной поток для обновления контролов
1
140 / 137 / 22
Регистрация: 16.02.2012
Сообщений: 453
27.01.2015, 23:29 7
Вы пробовали добавить в цикл Application.DoEvents() на каждую 1000 итераций к примеру?
0
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
27.01.2015, 23:40  [ТС] 8
Цитата Сообщение от SharpDeveloper Посмотреть сообщение
Вы пробовали добавить в цикл Application.DoEvents() на каждую 1000 итераций к примеру?
Да, пробовал. Никакого изменения.
Первым делом я это и сделал, до вывода в другой поток.
0
Эксперт .NETАвтор FAQ
10410 / 5140 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
27.01.2015, 23:56 9
Vitukr, Нельзя обращаться к визуальным компонентам из неосновного потока.
1
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
28.01.2015, 00:15  [ТС] 10
Цитата Сообщение от Storm23 Посмотреть сообщение
Vitukr, Нельзя обращаться к визуальным компонентам из неосновного потока.
А какое же тогда может быть решение при таком огромном цикле?
0
140 / 137 / 22
Регистрация: 16.02.2012
Сообщений: 453
28.01.2015, 00:31 11
Цитата Сообщение от Vitukr Посмотреть сообщение
Да, пробовал. Никакого изменения.
Это странно, покажите код.
0
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
28.01.2015, 00:38  [ТС] 12
Цитата Сообщение от SharpDeveloper Посмотреть сообщение
Это странно, покажите код.
Выше есть код. Там закомментирован Application.DoEvents() в циклах.
Закомментировал, когда добавлял поток.
0
Эксперт .NETАвтор FAQ
10410 / 5140 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
28.01.2015, 00:38 13
Цитата Сообщение от Vitukr Посмотреть сообщение
А какое же тогда может быть решение при таком огромном цикле?
А не нужно делать вычисления в визуальных компонентах. Визуальные компоненты - они для отображения данных. А вычисления нужно делать в самих данных, в модели.
1
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
28.01.2015, 00:48  [ТС] 14
Цитата Сообщение от Storm23 Посмотреть сообщение
А не нужно делать вычисления в визуальных компонентах. Визуальные компоненты - они для отображения данных. А вычисления нужно делать в самих данных, в модели.
Наверное так и придется переделывать, хотя не хотелось.
0
Эксперт .NET
5534 / 4298 / 1217
Регистрация: 12.10.2013
Сообщений: 12,332
Записей в блоге: 2
28.01.2015, 01:10 15
Vitukr, могу посоветовать хороший и (относительно) простой способ.
Создайте класс, унаследуйте его от BackgroundWorker-а, переопределите его виртуальный метод OnDoWork() и в этом методе реализуйте все что надо.
Так же у вашего класса будет два события: ProgressChanged и RunWorkerCompleted, подписавшись на которые, вы сможете обновлять интерфейс без лишних заморочек, напрямую.
1
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
08.03.2015, 15:14  [ТС] 16
Нашел причину, почему прогрес бар не работал даже в отдельном потоке.
Сейчас речь идет о приложении WPF, но думаю разницы где использовать backgroundWorker нет.
Вся проблема в добавлении кода
C#
1
Thread.Sleep(1);
Вот код метода, где запускается длительный цикл:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private int DoSlowProcess(int iterations, BackgroundWorker worker, DoWorkEventArgs e)
        {
            int result = 0;
            for (int i = 0; i <= iterations; i++)
            {
                if (worker != null)
                {
                    if (worker.CancellationPending)
                    {
                        e.Cancel = true;
                        return result;
                    }
                    if (worker.WorkerReportsProgress)
                    {
                        int percentComplete =
                        (int)((float)i / (float)iterations * 100);
                        worker.ReportProgress(percentComplete);
                    }
                }
                Thread.Sleep(1);
                result = i;
            }
            return result;
        }
Если закомментировать Thread.Sleep(1);
то ни прогрес бар, ни обновление текста в текстбоксе не работает.
Как и кнопка остановки цикла.
Если же не комментировать, то все работает.
Я снял видео, чтобы наиболее наглядно было видно это.

https://www.youtube.com/watch?... e=youtu.be

Теперь остался вопрос, как объяснить такое поведение?
В чем тут важность Thread.Sleep(1);
При этом, понятно, значение длительности сна потока можна ставить любое.

Хочу также отметить, что все примеры в Интернете использования backgroundWorker с прогрессбаром, но и наверное без прогрессбара, становятся ничтожными, если также комментировать Thread.Sleep(1);

Например, вот этот пример, который рекомендуется Майкрософтом: http://elegantcode.com/2009/07... to-the-ui/
Я комментировал в нем Thread.Sleep(1); и там ничего не работает в двух случаях.
Видимо, авторы всех этих примеров не знают этот тайный код Thread.Sleep(1);
0
Эксперт .NETАвтор FAQ
10410 / 5140 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
08.03.2015, 23:33 17
Vitukr, А сколько ядер в вашем процессоре?
Цитата Сообщение от Vitukr Посмотреть сообщение
Теперь остался вопрос, как объяснить такое поведение?
В чем тут важность Thread.Sleep(1);
Очень просто. Пока у вас крутится цикл, процессор не переключается на другие потоки. Но как только он встречает метод Thread.Sleep(1); он понимает, что текущий поток будет спать некторое время и производит переключение на другие потоки. Поэтому начинают работать прогрессбары и другие компоненты, работающие в другом потоке.
0
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
08.03.2015, 23:53  [ТС] 18
Цитата Сообщение от Storm23 Посмотреть сообщение
Очень просто. Пока у вас крутится цикл, процессор не переключается на другие потоки. Но как только он встречает метод Thread.Sleep(1); он понимает, что текущий поток будет спать некторое время и производит переключение на другие потоки. Поэтому начинают работать прогрессбары и другие компоненты, работающие в другом потоке.
Это я сразу понял, когда закомментировал/разкомментировал код сна.
Может я не правильно сформулировал вопрос.
Суть вопроса, зачем все эти "танцы" с backgroundWorker?
Ведь там же есть ReportProgress, который и без сна должен передавать значения в основной поток в процессе выполнения цикла?
По сути, Thread.Sleep(1); используется в примерах только для увеличения времени работы цикла, искусственно увеличивая время цикла. Так сказать, для демонстрационных целей.
Но, оказывается, этот код есть основной и для не демо программ.

При реально большом цикле использование Thread.Sleep(1); даже с 1 миллисекундой значительно будет увеличивать время работы цикла. Представьте, что одна итерация цикла без Thread.Sleep(1); будет меньше 1 миллисекунды, а реально там сотые доли миллисекунды, то добавление слип увеличивает время цикла в десятки раз.
Это просто недопустимо при программировании.
0
Эксперт .NETАвтор FAQ
10410 / 5140 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
09.03.2015, 00:12 19
Vitukr, Сколько ядер в вашем процессоре?
0
14 / 4 / 1
Регистрация: 08.11.2012
Сообщений: 189
Записей в блоге: 1
09.03.2015, 00:19  [ТС] 20
4 ядра.

Добавлено через 2 минуты
Вот здесь:
https://msdn.microsoft.com/ru-... kz4s1.aspx
приводится пример использования беграундворкер.
И в коде интересный момент:
C#
1
2
// Uncomment for testing.
                //System.Threading.Thread.Sleep(5);
Другими словами, этот код не обязательный для программы, а только для тестирования, т.е. посмотреть как работает.
Сейчас попробую воспроизвести.
0
09.03.2015, 00:19
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
09.03.2015, 00:19
Помогаю со студенческими работами здесь

Ошибка программы при выполнении цикла перебора
Добрый день коллеги ))) Помогите разобраться. При выполнении процедуры - цикла перебора...

Резкое зависание программы при частом запуске цикла
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics,...

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

Зависание интерфейса при выполнении запроса
Всем привет. Проблема в следующем. Есть кнопка, по ее нажатию отрабатывается запрос к базе...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru