Re[3]: Завершение работы потоков
От: Владек Россия Github
Дата: 10.03.06 11:20
Оценка: +2
Здравствуйте, ILS, Вы писали:

ILS>Здравствуйте, maxluzin, Вы писали:


M>>Во-первых,

M>>Метод Abort, по-моему, просто инициирует закрытие потока, но он не ждёт, пока закроется поток, а сразу возвращается в метод его вызвавший. Попробуй в этом обработчике события дождаться завершения всех потоков (смотри в сторону WaitHangle) и только потом выходить из метода (e.Cancel = false ? Так?)
ILS>Посмотрю.

M>>Во-вторых,

M>>К тому же, как видно из кода, у тебя переменная управляющая выходом из цикла curThreadCount открыта для свободного конкурентного доступа разных потоков одновременно. Это есть грубейшая ошибка, которая, скорее всего, и является источником проблемы. Обволоки её в lock() !
ILS>
ILS>//количество запущенных потоков
ILS>long curThreadCount;
ILS>

ILS>И попытка написать
ILS>
ILS>lock(curThreadCount) curThreadCount++;
ILS>

ILS>проваливается с сообщением об ошибке на этапе компиляции "'long' is not a reference type as required by the lock statement"
ILS>Да и мне не понятно, как могут несколько потоков при обращении к переменной свалить что-то.

Используйте:
Interlocked.Add(ref curThreadCount, 1);
Re: Завершение работы потоков
От: maxluzin Европа  
Дата: 10.03.06 09:33
Оценка: -1
Здравствуйте, 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() !
Re[2]: Завершение работы потоков
От: maxluzin Европа  
Дата: 10.03.06 10:10
Оценка: -1
Кстати, а где у тебя здесь инкремент 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).
Re: Завершение работы потоков
От: ILS Россия  
Дата: 15.03.06 08:20
Оценка: :)
Здравствуйте, ILS, Вы писали:

ILS>Что делать, как быть?


Решилось. Но решилось крайне некрасиво
На закрытие формы вставил малюсенькую паузу. И теперь программа завершается корректно с кодом 0.
[SRC c#] private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (isRunning)
{
isRunning = false;
Thread.Sleep(1);
}
}[/SRC]
И никакие ожидания работающих потоков не помогли. Где тут может быть косяк?
Re[2]: Завершение работы потоков
От: nzeemin Россия http://nzeemin.livejournal.com/
Дата: 23.10.07 05:31
Оценка: -1
Здравствуйте, Daniloff, Вы писали:

D>Ветка старая, но всё же...

D>Ситуация аналогична. Новичок в С#.
D>Работаю с потоками: при закрытии главной формы посылаю дочернему потоку Abort, но он продолжает работать! А поток ожидает NamedPipe. Выходит, нужно делать OVerlapped и какие-то эвенты отслеживать?..
D>Да, а как их сделать едиными для всех потоков-то?..

Все что делает Thread.Abort() — просто бросает исключение ThreadAbortException. Если у вас в рабочем треде где-то это исключение ловится и поток работает дальше (а это может быть просто catch(Exception ex)), то не удивляйтесь что поток после Abort() не завершается.
Завершение работы потоков
От: ILS Россия  
Дата: 10.03.06 07:40
Оценка:
Добрый день.

Недавно начал изучение .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--;
}

Что делать, как быть?
Re[2]: Завершение работы потоков
От: ILS Россия  
Дата: 10.03.06 11:03
Оценка:
Здравствуйте, maxluzin, Вы писали:

M>Во-первых,

M>Метод Abort, по-моему, просто инициирует закрытие потока, но он не ждёт, пока закроется поток, а сразу возвращается в метод его вызвавший. Попробуй в этом обработчике события дождаться завершения всех потоков (смотри в сторону WaitHangle) и только потом выходить из метода (e.Cancel = false ? Так?)
Посмотрю.

M>Во-вторых,

M>К тому же, как видно из кода, у тебя переменная управляющая выходом из цикла curThreadCount открыта для свободного конкурентного доступа разных потоков одновременно. Это есть грубейшая ошибка, которая, скорее всего, и является источником проблемы. Обволоки её в lock() !
//количество запущенных потоков
long curThreadCount;

И попытка написать
lock(curThreadCount) curThreadCount++;

проваливается с сообщением об ошибке на этапе компиляции "'long' is not a reference type as required by the lock statement"
Да и мне не понятно, как могут несколько потоков при обращении к переменной свалить что-то.

M> Иначе, в начале вся толпа стартовавших потоков захочет увеличить curThreadCount (с большой долей вероятности — одновременно), а в конце, когда ты всем потокам разошлёшь "аборты", они так же всей толпой страстно возжелают "опустить" твою curThreadCount на единичку, что приведёт к неправильному результату и к "висяку" программы на цикле. Поэтому, все данные, к которым возможен параллельный доступ из разных потоков должны закрываться или критическими секциями (lock) или взаимоисключающими блокировками (Mutex).

У меня количество работающих потоков ограничено. И даже если они "всей толпой страстно возжелают "опустить" твою curThreadCount на единичку", то максимум, мы уйдем в минуса, что для меня не страшно.
Re[3]: Завершение работы потоков
От: maxluzin Европа  
Дата: 10.03.06 11:19
Оценка:
Здравствуйте, 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, то ошибаетесь, так как после того, как один поток начнет эту операцию, у него управление может быть отобрано и передано другому потоку, и закончит он её только когда ему будет возвращено управление. Явно уже с искаженными данными. А это уже может привести к ошибке. Тем более, что эта одна маленькая пешка — переменная — может обвалить всю программу, если она ей управляет.
Re[4]: Завершение работы потоков
От: ILS Россия  
Дата: 13.03.06 14:39
Оценка:
Добавил на закрытие формы в FormClosing ожидание
[SRC c#]WaitHandle.WaitAll(wh);[/SRC]
Виснет на этой строке. Если же заменить WaitAll на WaitAny:
[SRC c#]while ((curThreadCount >= maxThreadCount) & (isRunning))
{
WaitHandle.WaitAny(wh, 100, false);
UpdateTotalInfo();
}[/SRC]
то вроде как работает, но ведь это некрасиво
Re: Завершение работы потоков
От: _FRED_ Черногория
Дата: 15.03.06 08:48
Оценка:
Здравствуйте, ILS, Вы писали:

ILS>public ArrayList threads;
ILS>...
ILS>private void btnStart_Click(object sender, EventArgs e)
ILS>{
ILS>  foreach (int i in checkedIndices)
ILS>  {                
                …
ILS>    threads.Add(t);
                …
ILS>  }
ILS>}
…
ILS>private void ThreadDone(int index) //функция вызывается когда, когда поток завершает работу
ILS>{
                …
ILS>  threads.Remove(Thread.CurrentThread);
                …
ILS>}

ILS>Что делать, как быть?

Для начала, синхронизируй доступ к threads.
... << RSDN@Home 1.2.0 alpha rev. 648>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Re: Завершение работы потоков
От: ILS Россия  
Дата: 16.03.06 09:04
Оценка:
Здравствуйте, ILS, Вы писали:

ILS>Добрый день.


ILS>Недавно начал изучение .NET и вот взялся за многопоточное приложение. Пишу программу, которая по команде пользователя запускает на выполнение некоторое количество потоков (ограниченное сверху). По нажатию кнопки взводится флажок и все запущенные потоки прекращают свою работу. Если флажок не появляется, то поток просто доделывает свое дело и также прекращает работу.

ILS>Проблема появляется, когда во время работы потоков пользователь закрывает окно. Всё виснет.

Проблема была в локах. То, что валилось при Invoke, происходило из-за того, что isRunning не лочилось. Как только я стал лочить isRunning, всё стало работать хорошо. Привожу код:
readonly object runLock = new object(); //объект для блокирования isRunning
private 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
Re: Завершение работы потоков
От: Daniloff  
Дата: 22.10.07 20:52
Оценка:
Ветка старая, но всё же...
Ситуация аналогична. Новичок в С#.
Работаю с потоками: при закрытии главной формы посылаю дочернему потоку Abort, но он продолжает работать! А поток ожидает NamedPipe. Выходит, нужно делать OVerlapped и какие-то эвенты отслеживать?..
Да, а как их сделать едиными для всех потоков-то?..
Re[2]: Завершение работы потоков
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 23.10.07 05:41
Оценка:
Здравствуйте, Daniloff, Вы писали:

D>Работаю с потоками: при закрытии главной формы посылаю дочернему потоку Abort, но он продолжает работать!


http://rsdn.ru/article/dotnet/CSThreading2.xml
Автор(ы): Joseph Albahari
Дата: 27.06.2007
Окончание статьи, опубликованной в RSDN Magazine #1-2007. Рассматриваются особенности взаимодействия с апартаментами, потоковые таймеры, пулы потоков, BackgroundWorker, асинхронные методы и делегаты.
В статье использован материал из книги Joseph Albahari, Ben Albahari "C# 3.0 in a Nutshell" — http://www.oreilly.com/catalog/9780596527570/
... << RSDN@Home 1.2.0 alpha rev. 780>>
Re[3]: Завершение работы потоков
От: _Morpheus_  
Дата: 23.10.07 09:40
Оценка:
Здравствуйте, ILS, Вы писали:

M>> Иначе, в начале вся толпа стартовавших потоков захочет увеличить curThreadCount (с большой долей вероятности — одновременно), а в конце, когда ты всем потокам разошлёшь "аборты", они так же всей толпой страстно возжелают "опустить" твою curThreadCount на единичку, что приведёт к неправильному результату и к "висяку" программы на цикле. Поэтому, все данные, к которым возможен параллельный доступ из разных потоков должны закрываться или критическими секциями (lock) или взаимоисключающими блокировками (Mutex).

ILS>У меня количество работающих потоков ограничено. И даже если они "всей толпой страстно возжелают "опустить" твою curThreadCount на единичку", то максимум, мы уйдем в минуса, что для меня не страшно.

проблема заключается вовсе не в этом, а в том что без синхронизации два потока могут одновременно изменить переменную, но изменения сделанные одним потоком будут тутже затерты другим потоком, как будто первый поток инчего и не менял
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[3]: Завершение работы потоков
От: _Morpheus_  
Дата: 23.10.07 09:52
Оценка:
Здравствуйте, nzeemin, Вы писали:


N>Все что делает Thread.Abort() — просто бросает исключение ThreadAbortException. Если у вас в рабочем треде где-то это исключение ловится и поток работает дальше (а это может быть просто

N> catch(Exception ex)), то не удивляйтесь что поток после Abort() не завершается.

исключение ThreadAbort невозможно отловить, как только завершится блок catch обрабатывающий его, оно будет тутже сгенерировано поновой — это непрерываемое исключение, оно будет генерироваться до тех пор пока управление не покинет процедуру потока

А Abort не работает по совсем другой причине — Abort не может прервать выполнение unmanaged вызова, сделанного гдето внутрях фреймворка, в данном случае скорее всего проиходит блокирующий вызов ReadFile, поэтому Abort прервать его не может
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[4]: Завершение работы потоков
От: Daniloff  
Дата: 23.10.07 10:01
Оценка:
Здравствуйте, _Morpheus_, Вы писали:

_M_>А Abort не работает по совсем другой причине — Abort не может прервать выполнение unmanaged вызова, сделанного гдето внутрях фреймворка, в данном случае скорее всего проиходит блокирующий вызов ReadFile, поэтому Abort прервать его не может


Почти угадали. Происходит ConnectNamedPipe. Но сделать его неблокирующим — сил нет: все эти NativeOverlapped почти что недокументированы (в MSDN ни одного внятного примера не нашел)
Re[5]: Завершение работы потоков
От: _Morpheus_  
Дата: 23.10.07 10:09
Оценка:
Здравствуйте, Daniloff, Вы писали:

_M_>>А Abort не работает по совсем другой причине — Abort не может прервать выполнение unmanaged вызова, сделанного гдето внутрях фреймворка, в данном случае скорее всего проиходит блокирующий вызов ReadFile, поэтому Abort прервать его не может


D>Почти угадали. Происходит ConnectNamedPipe. Но сделать его неблокирующим — сил нет: все эти NativeOverlapped почти что недокументированы (в MSDN ни одного внятного примера не нашел)


посмотри как реализован IpcChannel
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[6]: Завершение работы потоков
От: Daniloff  
Дата: 23.10.07 10:59
Оценка:
Здравствуйте, _Morpheus_, Вы писали:

_M_>посмотри как реализован IpcChannel


Вот такую дрянь пишет.
error CS0234: The type or namespace name 'Ipc' does not exist in the namespace 'System.Runtime.Remoting.Channels'


В Object Browser есть, а в контекстном наборе нет...
Re[7]: Завершение работы потоков
От: Daniloff  
Дата: 23.10.07 11:13
Оценка:
С ошибкой разобрался. Умнеем помаленьку...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.