Здравствуйте, ILS, Вы писали:
ILS>Здравствуйте, maxluzin, Вы писали:
M>>Во-первых, M>>Метод Abort, по-моему, просто инициирует закрытие потока, но он не ждёт, пока закроется поток, а сразу возвращается в метод его вызвавший. Попробуй в этом обработчике события дождаться завершения всех потоков (смотри в сторону WaitHangle) и только потом выходить из метода (e.Cancel = false ? Так?) ILS>Посмотрю.
M>>Во-вторых, M>>К тому же, как видно из кода, у тебя переменная управляющая выходом из цикла curThreadCount открыта для свободного конкурентного доступа разных потоков одновременно. Это есть грубейшая ошибка, которая, скорее всего, и является источником проблемы. Обволоки её в lock() ! ILS>
ILS>проваливается с сообщением об ошибке на этапе компиляции "'long' is not a reference type as required by the lock statement" ILS>Да и мне не понятно, как могут несколько потоков при обращении к переменной свалить что-то.
Здравствуйте, ILS, Вы писали:
ILS>Добрый день.
ILS>Недавно начал изучение .NET и вот взялся за многопоточное приложение. Пишу программу, которая по команде пользователя запускает на выполнение некоторое количество потоков (ограниченное сверху). По нажатию кнопки взводится флажок и все запущенные потоки прекращают свою работу. Если флажок не появляется, то поток просто доделывает свое дело и также прекращает работу. ILS>Проблема появляется, когда во время работы потоков пользователь закрывает окно. Всё виснет.
ILS>} ILS>[/c#] ILS>На закрытие формы я пробегаю по массиву и каждый поток останавливаю ILS>
ILS>private void FrmLogCollector_FormClosing(object sender, FormClosingEventArgs e)
ILS>{
ILS> if (isRunning)
ILS> {
ILS> isRunning = false; //сбрасываем флажок
ILS> foreach (Thread t in threads)
ILS> {
ILS> t.Abort();
ILS> //t.Join(); //если убрать коммент, то виснет еще тут. если коммент стоит, то виснет после выхода из этой функции
ILS> }
ILS> }
ILS>}
ILS>
ILS>Если не пробегать по массиву, то падает при попытке обратиться к Invoke.
Во-первых,
Метод Abort, по-моему, просто инициирует закрытие потока, но он не ждёт, пока закроется поток, а сразу возвращается в метод его вызвавший. Попробуй в этом обработчике события дождаться завершения всех потоков (смотри в сторону WaitHangle) и только потом выходить из метода (e.Cancel = false ? Так?)
ILS>
ILS>private void ThreadDone(int index) //функция вызывается когда, когда поток завершает работу
ILS>{
ILS> SafeThreadDoneDelegate d = new SafeThreadDoneDelegate(SafeThreadDone);
ILS> Invoke(d, new object[] { index });
ILS> threads.Remove(Thread.CurrentThread);
ILS> curThreadCount--;
ILS>}
ILS>
ILS>Что делать, как быть?
Во-вторых,
К тому же, как видно из кода, у тебя переменная управляющая выходом из цикла curThreadCount открыта для свободного конкурентного доступа разных потоков одновременно. Это есть грубейшая ошибка, которая, скорее всего, и является источником проблемы. Обволоки её в lock() !
Кстати, а где у тебя здесь инкремент curThreadCount ? :
private void btnStart_Click(object sender, EventArgs e)
{
foreach (int i in checkedIndices)
{
MyThreadClass clc = new MyThreadClass(paramString1,
new CallbackFunction(ThreadStart), //функция для обновления инфы формы при запуске потокаnew CallbackFunction(ThreadDone)); //функция для обновления инфы формы при завершении работы потока (там мы удаляем поток из массива и уменьшаем число запущенных)
Thread t = new Thread(new ThreadStart(clc.CollectComputerLog));
t.IsBackground = true;
t.Start();
threads.Add(t);
while ((curThreadCount >= maxThreadCount) & (isRunning)) //проверка на достижение максимального количества запущенных потоков
UpdateTotalInfo();
if (!isRunning) //тот самый флажокbreak;
}
В этом случае нужно также сделать lock:
lock (curThreadCount) curThreadCount++;
Иначе, в начале вся толпа стартовавших потоков захочет увеличить curThreadCount (с большой долей вероятности — одновременно), а в конце, когда ты всем потокам разошлёшь "аборты", они так же всей толпой страстно возжелают "опустить" твою curThreadCount на единичку, что приведёт к неправильному результату и к "висяку" программы на цикле. Поэтому, все данные, к которым возможен параллельный доступ из разных потоков должны закрываться или критическими секциями (lock) или взаимоисключающими блокировками (Mutex).
Здравствуйте, ILS, Вы писали:
ILS>Что делать, как быть?
Решилось. Но решилось крайне некрасиво
На закрытие формы вставил малюсенькую паузу. И теперь программа завершается корректно с кодом 0.
[SRC c#] private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (isRunning)
{
isRunning = false;
Thread.Sleep(1);
}
}[/SRC]
И никакие ожидания работающих потоков не помогли. Где тут может быть косяк?
Здравствуйте, Daniloff, Вы писали:
D>Ветка старая, но всё же... D>Ситуация аналогична. Новичок в С#. D>Работаю с потоками: при закрытии главной формы посылаю дочернему потоку Abort, но он продолжает работать! А поток ожидает NamedPipe. Выходит, нужно делать OVerlapped и какие-то эвенты отслеживать?.. D>Да, а как их сделать едиными для всех потоков-то?..
Все что делает Thread.Abort() — просто бросает исключение ThreadAbortException. Если у вас в рабочем треде где-то это исключение ловится и поток работает дальше (а это может быть просто catch(Exception ex)), то не удивляйтесь что поток после Abort() не завершается.
Недавно начал изучение .NET и вот взялся за многопоточное приложение. Пишу программу, которая по команде пользователя запускает на выполнение некоторое количество потоков (ограниченное сверху). По нажатию кнопки взводится флажок и все запущенные потоки прекращают свою работу. Если флажок не появляется, то поток просто доделывает свое дело и также прекращает работу.
Проблема появляется, когда во время работы потоков пользователь закрывает окно. Всё виснет.
При запуске потока я его запоминаю в массиве
public ArrayList threads;
...
private void btnStart_Click(object sender, EventArgs e)
{
foreach (int i in checkedIndices)
{
MyThreadClass clc = new MyThreadClass(paramString1,
new CallbackFunction(ThreadStart), //функция для обновления инфы формы при запуске потокаnew CallbackFunction(ThreadDone)); //функция для обновления инфы формы при завершении работы потока (там мы удаляем поток из массива и уменьшаем число запущенных)
Thread t = new Thread(new ThreadStart(clc.CollectComputerLog));
t.IsBackground = true;
t.Start();
threads.Add(t);
while ((curThreadCount >= maxThreadCount) & (isRunning)) //проверка на достижение максимального количества запущенных потоков
UpdateTotalInfo();
if (!isRunning) //тот самый флажокbreak;
}
На закрытие формы я пробегаю по массиву и каждый поток останавливаю
private void FrmLogCollector_FormClosing(object sender, FormClosingEventArgs e)
{
if (isRunning)
{
isRunning = false; //сбрасываем флажокforeach (Thread t in threads)
{
t.Abort();
//t.Join(); //если убрать коммент, то виснет еще тут. если коммент стоит, то виснет после выхода из этой функции
}
}
}
Если не пробегать по массиву, то падает при попытке обратиться к Invoke.
private void ThreadDone(int index) //функция вызывается когда, когда поток завершает работу
{
SafeThreadDoneDelegate d = new SafeThreadDoneDelegate(SafeThreadDone);
Invoke(d, new object[] { index });
threads.Remove(Thread.CurrentThread);
curThreadCount--;
}
Здравствуйте, maxluzin, Вы писали:
M>Во-первых, M>Метод Abort, по-моему, просто инициирует закрытие потока, но он не ждёт, пока закроется поток, а сразу возвращается в метод его вызвавший. Попробуй в этом обработчике события дождаться завершения всех потоков (смотри в сторону WaitHangle) и только потом выходить из метода (e.Cancel = false ? Так?)
Посмотрю.
M>Во-вторых, M>К тому же, как видно из кода, у тебя переменная управляющая выходом из цикла curThreadCount открыта для свободного конкурентного доступа разных потоков одновременно. Это есть грубейшая ошибка, которая, скорее всего, и является источником проблемы. Обволоки её в lock() !
проваливается с сообщением об ошибке на этапе компиляции "'long' is not a reference type as required by the lock statement"
Да и мне не понятно, как могут несколько потоков при обращении к переменной свалить что-то.
M> Иначе, в начале вся толпа стартовавших потоков захочет увеличить curThreadCount (с большой долей вероятности — одновременно), а в конце, когда ты всем потокам разошлёшь "аборты", они так же всей толпой страстно возжелают "опустить" твою curThreadCount на единичку, что приведёт к неправильному результату и к "висяку" программы на цикле. Поэтому, все данные, к которым возможен параллельный доступ из разных потоков должны закрываться или критическими секциями (lock) или взаимоисключающими блокировками (Mutex).
У меня количество работающих потоков ограничено. И даже если они "всей толпой страстно возжелают "опустить" твою curThreadCount на единичку", то максимум, мы уйдем в минуса, что для меня не страшно.
Здравствуйте, ILS, Вы писали:
ILS>Здравствуйте, maxluzin, Вы писали:
M>>Во-первых, M>>Метод Abort, по-моему, просто инициирует закрытие потока, но он не ждёт, пока закроется поток, а сразу возвращается в метод его вызвавший. Попробуй в этом обработчике события дождаться завершения всех потоков (смотри в сторону WaitHangle) и только потом выходить из метода (e.Cancel = false ? Так?) ILS>Посмотрю.
Посмотри, посмотри... Мне с потоками в .Net-e не очень часто приходится работать (явно), а вот в Win32 я их пользовал очень даже активно, и там считалось "дурным тоном" "вываливаться" из процесса не дождавшись завершения всех потоков ЯВНО в своём коде. То, что дот-нетовская рантайм-машина всё подчистит (кроме открытых незащищенных хэндлов ресурсов), на это не стоит слишком уповать — рано или поздно такой стиль программирования приведёт к непредсказуемым и нестабильным (невоспроизводимым) ошибкам, которые появляются чёрт знает когда и чёрт знает по какой причине. Отловить такую проблему в распухшей и разросшейся со временем программе — ох, как нелегко! Лучше всё сразу делать "по понятиям" и как "мама учила".
ILS>У меня количество работающих потоков ограничено. И даже если они "всей толпой страстно возжелают "опустить" твою curThreadCount на единичку", то максимум, мы уйдем в минуса, что для меня не страшно.
В данном конкретном случае может и не страшно. Страшно — это такая привычка, так делать. По-крайней мере в мультипоточном программировании — это "явный косяк от правил"
lock, действительно, работает только с ссылочными объектами (забыл, ссори). Делай тогда так (в методе класса):
lock (this)
{
my_counter++;
}
Сделай так, как сказал, а потом напиши, заработало или нет. Интересно ведь. Зря что ли время терял.
Re[3]: Завершение работы потоков
От:
Аноним
Дата:
10.03.06 11:46
Оценка:
ILS>Да и мне не понятно, как могут несколько потоков при обращении к переменной свалить что-то.
Тут Вы в корне заблуждаетесь. Запросто! Как Вы думаете, сколько команд процессора занимает в .Net'e операция инкремента или декремента переменной? Если думаете, как в старом добром "С", что это переводится один в один в одну команду процессора inc/dec, то ошибаетесь, так как после того, как один поток начнет эту операцию, у него управление может быть отобрано и передано другому потоку, и закончит он её только когда ему будет возвращено управление. Явно уже с искаженными данными. А это уже может привести к ошибке. Тем более, что эта одна маленькая пешка — переменная — может обвалить всю программу, если она ей управляет.
Добавил на закрытие формы в FormClosing ожидание
[SRC c#]WaitHandle.WaitAll(wh);[/SRC]
Виснет на этой строке. Если же заменить WaitAll на WaitAny:
[SRC c#]while ((curThreadCount >= maxThreadCount) & (isRunning))
{
WaitHandle.WaitAny(wh, 100, false);
UpdateTotalInfo();
}[/SRC]
то вроде как работает, но ведь это некрасиво
Здравствуйте, ILS, Вы писали:
ILS>Добрый день.
ILS>Недавно начал изучение .NET и вот взялся за многопоточное приложение. Пишу программу, которая по команде пользователя запускает на выполнение некоторое количество потоков (ограниченное сверху). По нажатию кнопки взводится флажок и все запущенные потоки прекращают свою работу. Если флажок не появляется, то поток просто доделывает свое дело и также прекращает работу. ILS>Проблема появляется, когда во время работы потоков пользователь закрывает окно. Всё виснет.
Проблема была в локах. То, что валилось при Invoke, происходило из-за того, что isRunning не лочилось. Как только я стал лочить isRunning, всё стало работать хорошо. Привожу код:
readonly object runLock = new object(); //объект для блокирования isRunningprivate bool _isRunning;
public bool isRunning
{
get { lock (runLock) { return _isRunning; } } //вот эти локи и нужны были для корректной работы!!!set { lock (runLock) { _isRunning = value; } }
}
private void FrmLogCollector_FormClosing(object sender, FormClosingEventArgs e)
{
if (isRunning)
{
isRunning = false; //и всё. никаких ожидающих WaitAll
}
}
private bool ThreadUpdateInfo(int index, int progress)
{
if (isRunning)
{
object res = Invoke(new SafeUpdateInfoDelegate(SafeUpdateInfo), new object[] { index, progress });
return (bool)res;
}
else
return false;
}
private void btnStart_Click(object sender, EventArgs e)
{
foreach (int i in checkedIndices)
{
MyThreadClass clc = new MyThreadClass(paramString1,
new CallbackFunction(ThreadUpdateInfo)); //функция для обновления инфы при работе потока
Thread t = new Thread(new ThreadStart(clc.CollectComputerLog));
t.IsBackground = true;
t.Start();
while ((curThreadCount >= maxThreadCount) & (isRunning)) //проверка на достижение максимального количества запущенных потоков
UpdateTotalInfo();
if (!isRunning) //тот самый флажокbreak;
}
И всё работает. Спасибо всем. Как обычно, оказалось RTFM
Ветка старая, но всё же...
Ситуация аналогична. Новичок в С#.
Работаю с потоками: при закрытии главной формы посылаю дочернему потоку Abort, но он продолжает работать! А поток ожидает NamedPipe. Выходит, нужно делать OVerlapped и какие-то эвенты отслеживать?..
Да, а как их сделать едиными для всех потоков-то?..
Здравствуйте, ILS, Вы писали:
M>> Иначе, в начале вся толпа стартовавших потоков захочет увеличить curThreadCount (с большой долей вероятности — одновременно), а в конце, когда ты всем потокам разошлёшь "аборты", они так же всей толпой страстно возжелают "опустить" твою curThreadCount на единичку, что приведёт к неправильному результату и к "висяку" программы на цикле. Поэтому, все данные, к которым возможен параллельный доступ из разных потоков должны закрываться или критическими секциями (lock) или взаимоисключающими блокировками (Mutex). ILS>У меня количество работающих потоков ограничено. И даже если они "всей толпой страстно возжелают "опустить" твою curThreadCount на единичку", то максимум, мы уйдем в минуса, что для меня не страшно.
проблема заключается вовсе не в этом, а в том что без синхронизации два потока могут одновременно изменить переменную, но изменения сделанные одним потоком будут тутже затерты другим потоком, как будто первый поток инчего и не менял
N>Все что делает Thread.Abort() — просто бросает исключение ThreadAbortException. Если у вас в рабочем треде где-то это исключение ловится и поток работает дальше (а это может быть просто N> catch(Exception ex)), то не удивляйтесь что поток после Abort() не завершается.
исключение ThreadAbort невозможно отловить, как только завершится блок catch обрабатывающий его, оно будет тутже сгенерировано поновой — это непрерываемое исключение, оно будет генерироваться до тех пор пока управление не покинет процедуру потока
А Abort не работает по совсем другой причине — Abort не может прервать выполнение unmanaged вызова, сделанного гдето внутрях фреймворка, в данном случае скорее всего проиходит блокирующий вызов ReadFile, поэтому Abort прервать его не может
Здравствуйте, _Morpheus_, Вы писали:
_M_>А Abort не работает по совсем другой причине — Abort не может прервать выполнение unmanaged вызова, сделанного гдето внутрях фреймворка, в данном случае скорее всего проиходит блокирующий вызов ReadFile, поэтому Abort прервать его не может
Почти угадали. Происходит ConnectNamedPipe. Но сделать его неблокирующим — сил нет: все эти NativeOverlapped почти что недокументированы (в MSDN ни одного внятного примера не нашел)
Здравствуйте, Daniloff, Вы писали:
_M_>>А Abort не работает по совсем другой причине — Abort не может прервать выполнение unmanaged вызова, сделанного гдето внутрях фреймворка, в данном случае скорее всего проиходит блокирующий вызов ReadFile, поэтому Abort прервать его не может
D>Почти угадали. Происходит ConnectNamedPipe. Но сделать его неблокирующим — сил нет: все эти NativeOverlapped почти что недокументированы (в MSDN ни одного внятного примера не нашел)