Форум программистов, компьютерный форум, киберфорум
C# Windows Forms
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.74/50: Рейтинг темы: голосов - 50, средняя оценка - 4.74
0 / 0 / 0
Регистрация: 05.05.2014
Сообщений: 10
1

Ждать завершения потока (без зависания формы)

01.06.2014, 22:50. Показов 9686. Ответов 5
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Здравствуйте товарищи!

Столкнулся с такой вот проблемой.
Пишу программу, которая запускает через cmd.exe некие команды. Форма зависает на прослушивании ответа от cmd (Оно и понятно, там цикл прослушивания и вывода результата) и отвисает после выполнения.
Для решения этой проблемы я использовал поток, но теперь программа не дожидается выполнения функции из потока, а просто запускает его и продолжает работать дальше.

Подскажите пожалуйста как сделать так, чтобы запускался отдельный поток, но программа ждала (без зависания формы) его окончания (ну или какого-либо события).

Структура работы:
Как сейчас:
Пользователь выбрал команду (например.ping ya.ru) -> Запускается поток с функцией cmd.exe + эта команда -> Программа НЕ ЖДЕТ завершения потока и продолжает выполнять код.
Как необходимо:
Пользователь выбрал команду (например.ping ya.ru) -> Запускается поток с функцией cmd.exe + эта команда -> Программа ЖДЕТ завершения потока и продолжает выполнять код.
Вот код функции командной строки :
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
        public void RunCmd(object command)
        {
            Thread newThread = new Thread(delegate() { DoWork(command); });
            newThread.Start();
            // Тут нужно ждать ответа от функции DoWork
        }
 
        public void DoWork(object command)
        {
            Process proc = new Process()
            {
                StartInfo = new ProcessStartInfo("cmd.exe", "/c" + command)
                {
                    StandardOutputEncoding = Encoding.GetEncoding(866),
                    RedirectStandardOutput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    WindowStyle = ProcessWindowStyle.Hidden
                }
            };
            proc.Start();
 
            if (!proc.StartInfo.RedirectStandardOutput)
                return;
 
            string line;
            StreamReader sr = proc.StandardOutput;
            while (!sr.EndOfStream)
            {
                line = sr.ReadLine();
                richTextBox1.Invoke(new MethodInvoker(() =>
                {
                    richTextBox1.Text += line + "\n";
                }), null);
            }
        }
PS: newThread.Join() тоже вешает форму.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
01.06.2014, 22:50
Ответы с готовыми решениями:

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

Как сделать задержку в программе без зависания формы
Доброго времени суток :) Никак не получается решить одну задачу. Допустим, есть форма с одной...

Ждать завершения backgroundWorker
Возможно ли в основном потоке подождать пока выполнится backgroundworker. например... private...

Затемнение формы без приостановок основного потока
Нужно организовать плавное заметнее всей формы (включая все элементы), причем это не должно трогать...

5
Life Builder
532 / 496 / 374
Регистрация: 12.01.2011
Сообщений: 1,754
01.06.2014, 23:06 2
Вот так?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
Process proc = new Process()
            {
                StartInfo = new ProcessStartInfo("cmd.exe", "/c" + command)
                {
                    StandardOutputEncoding = Encoding.GetEncoding(866),
                    RedirectStandardOutput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    WindowStyle = ProcessWindowStyle.Hidden
                }
            };
            proc.Start();
            proc.WaitForExit();
Добавлено через 5 минут
или может лучше повесить обработчик?
C#
1
2
3
4
5
6
7
8
9
10
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
        //....................................
 
        private void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            richTextBox1.Invoke(new Action(() =>
            {
                richTextBox1.Text += e.Data + "\n";
            }), null);
        }
0
0 / 0 / 0
Регистрация: 05.05.2014
Сообщений: 10
03.06.2014, 10:27  [ТС] 3
sk007, Ваш способ к сожалению не работает.
А не работает, как я понимаю, потому, что вся функция уже идет в отдельном потоке и основному коду по барабану, выполнилась она уже или нет. То есть эти проверки и обработчики не могут "заморозить" основной код из своего потока. Поправьте если ошибаюсь.
Вопрос актуален.

Добавлено через 2 часа 32 минуты
В общем решилась проблема проще простого.
Может и немного по индуски, но работает как нужно.

Приведу решение для последующих поколений
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
// bool checkThread = false;        // Внести в глобальные переменные, это наш сигнал завершения выполнения потока
 
public void RunCmd(object command)
        {
            Thread newThread = new Thread(delegate() { DoWork(command); });
            newThread.Start();
            while (checkThread == false)        // Цикл не дающий выполняться коду дальше пока
            {                                            //  сигнал checkThread не будет true
                Application.DoEvents();
            }
            checkThread = false;              // Возвращаем сигнал на исходную
     // ....................                         //  Продолжаем выполнять код
        }
 
public void DoWork(object command)
        {
            Process proc = new Process()
            {
                StartInfo = new ProcessStartInfo("cmd.exe", "/c" + command)
                {
                    StandardOutputEncoding = Encoding.GetEncoding(866),
                    RedirectStandardOutput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    WindowStyle = ProcessWindowStyle.Hidden
                }
            };
            proc.Start();
 
            if (!proc.StartInfo.RedirectStandardOutput)
                return;
 
            string line;
            StreamReader sr = proc.StandardOutput;
            while (!sr.EndOfStream)
            {
                line = sr.ReadLine();
                richTextBox1.Invoke(new MethodInvoker(() =>
                {
                    richTextBox1.Text += line + "\n";
                }), null);
            }
            checkThread = true;     // Сигнал (функция выполнена)
        }
0
995 / 893 / 354
Регистрация: 24.03.2014
Сообщений: 2,381
Записей в блоге: 2
03.06.2014, 11:34 4
6eH, забудьте про Application.DoEvents(), про "ручной" запуск потоков тоже, ThreadPool есть, он разберётся что создать.
что касается ожидания, Вы неправильно подходите к решению задачи, можно и просто RichTextBox для чтения сделать, коль уж нужно дождаться.

Не по теме:

Цитата Сообщение от 6eH Посмотреть сообщение
В общем решилась проблема проще простого.
Скорее убилась... причём с разбега

1
0 / 0 / 0
Регистрация: 05.05.2014
Сообщений: 10
04.06.2014, 00:43  [ТС] 5
Был бы благодарен за наглядный пример.
Обязательно почитаю про ThreadPool.
И что значит : "... RichTextBox для чтения сделать ..."
0
995 / 893 / 354
Регистрация: 24.03.2014
Сообщений: 2,381
Записей в блоге: 2
04.06.2014, 09:22 6
Создайте формочку Form1, киньте туда следующее:
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
126
127
128
129
130
131
132
133
134
135
        // Элементы управления
        // кнопка запуска
        Button btnStart;
        // текстовое поле для команды
        TextBox tbCommand;
        // RichTextBox с результатами
        RichTextBox rtbResult;
        // Панелька для поля и кнопки
        Panel pnlCommads;
 
        public Form1()
        {
            InitializeComponent();
 
            // инициализируем панельку
            pnlCommads = new Panel()
            {
                Height = 22,
                // будет прибита к нижней части формы
                Dock = DockStyle.Bottom
            };
            // инициализируем кнопку
            btnStart = new Button()
            {
                Width = 60,
                // будет прибита к правой части панели
                Dock = DockStyle.Right,
                Text = "Start",
            };
            // событие запуска команды (нажатие на кнопку)
            btnStart.Click += btnStart_Click;
            // инициализируем поле для команды
            tbCommand = new TextBox()
            {
                // заполнит всё свободное пространство
                Dock = DockStyle.Fill,
                // команда по-умолчанию
                Text = "ping 127.0.0.1"
            };
            // событие изменения текста в поле
            tbCommand.TextChanged += tbCommand_TextChanged;
            // инициализируем RichTextBox
            rtbResult = new RichTextBox()
            {
                // займёт всё свободное пространство
                Dock = DockStyle.Fill,
                // только для чтения
                ReadOnly = true,
                // чтобы нельзя было переключиться Tab'ом
                TabStop = false
            };
 
            // текстовое поле и кнопку кладём на панель
            pnlCommads.Controls.AddRange(new Control[] { btnStart, tbCommand });
            // RichTextBox и панель кладём на форму
            // порядок добавления важен, так как используем свойство Dock у элементов
            this.Controls.AddRange(new Control[] { rtbResult, pnlCommads });
            // изменяем заголовок формы
            this.Text = "Программа запущена";
        }
 
        /// <summary>
        /// Событие запуска команды
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void btnStart_Click(object sender, EventArgs e)
        {
            // изменяем заголовок формы
            this.Text = "Выполняем команду";
            // делаем панель недоступной, чтобы не нажали ещё разок
            pnlCommads.Enabled = false;
            // запуускаем выполнение команды в отдельном потоке
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), tbCommand.Text);
        }
 
        /// <summary>
        /// Выполнение команды
        /// </summary>
        /// <param name="command">Команда</param>
        private void DoWork(object command)
        {
            Process proc = new Process()
            {
                StartInfo = new ProcessStartInfo("cmd.exe", String.Format("/c \"{0}\"", command as string))
                {
                    StandardOutputEncoding = Encoding.GetEncoding(866),
                    RedirectStandardOutput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    WindowStyle = ProcessWindowStyle.Hidden
                }
            };
            proc.Start();
 
            if (proc.StartInfo.RedirectStandardOutput)
            {
                string line;
                StreamReader sr = proc.StandardOutput;
                // Если процесс ещё не завершился
                while (!proc.HasExited)
                {
                    // Если есть что показать
                    while (!sr.EndOfStream)
                    {
                        line = sr.ReadLine();
                        rtbResult.Invoke(new MethodInvoker(() =>
                        {
                            rtbResult.AppendText(line + "\n");
                        }), null);
                    }
                    // даём передышку потоку, пока в консоли появятся данные
                    Thread.Sleep(10);
                }
            }
            // Сигнализируем о завершении выполнения команды
            this.Invoke(new MethodInvoker(CommandFinished));
        }
 
        /// <summary>
        /// Завершение выполнения команды
        /// </summary>
        private void CommandFinished()
        {
            // делаем панельку вновь активной
            pnlCommads.Enabled = true;
            // изменяем заголовок
            this.Text = "Команда выполнена";
        }
 
        void tbCommand_TextChanged(object sender, EventArgs e)
        {
            // если текст в поле отсутствует, делаем кнопку неактивной
            btnStart.Enabled = tbCommand.TextLength > 0;
        }
1
04.06.2014, 09:22
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
04.06.2014, 09:22
Помогаю со студенческими работами здесь

Если запустить 2 потока, будет ли один ждать, пока завершится другой
Здравствуйте! Возможно вопрос глупый, но вот никак не пойму Есть цыкл каторый выполняеться n-е...

Ожидание завершения потока
private void button1_Click(object sender, EventArgs e) { ...

Как правильно дождаться завершения потока?
Здравствуйте у меня возник вопрос как дождаться завершения потока метод Thread.join(); тормозит...

Десериализация. Конец потока обнаружен до завершения разбора
При десереализации возникает следующая ошибка: Объект, не помечен, как сериализуемый. Конец потока...


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

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