Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Как прибить зависший поток?

30K
13 ноября 2007 года
Mmonstr
9 / / 14.09.2007
Вот такая ситуация. К примеру вы создаете поток используя интерфейс System.Threading. Пока поток работает нормально, его можно критически завершить вызовом thread.Abort(). Но если возникает ситуация, что выполнение потока приостановлено (к примеру, вследствие зависания, спровоцированного вызовом сторонней функции, в частности, элемента ActiveX), то этот метод не может принудительно терминировать поток, как это было бы в случае вызова Api функции TerminateThread(), если бы мы работали в обычном Win32. Мне кажется, здесь возможна утечка памяти, т.к. при создании нового потока старый может оставаться в памяти, его ресурсы не освобождаются до обработки Aborted. Искал пути решения достаточно долго, но так ничего и не нашел. Кто-нибудь сталкивался с аналогичной проблемой? Ведь должен же быть предусмотрен метод экстренного завершения потока? Я в .net перешел сравнительно недавно, поэтому прошу экспертов поделиться информацией, заранее спасибо за ответы
5
14 ноября 2007 года
hardcase
4.5K / / 09.08.2005
Помню, наступал на такие грабли.
Если в момент вызова метода Abort поток выполняет управляемый код, то внутри него будет порождено исключение ThreadAbort и он корректно завершится. В случае, когда поток выполняет неуправляемый код, исключение ThreadAbort будет порождено только после выхода из неуправлемой секции.


Во втором случае завершить поток можно только вызовом Win32 API функции
 
Код:
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
30K
14 ноября 2007 года
Mmonstr
9 / / 14.09.2007
Да, видимо это единственный сейчас доступный метод.. Тогда возникает еще 2 вопроса: откуда взять хендл этого потока (в классе TThread не нашел.. может я просто не туда смотрел?) и как определить, жив ли выбранный поток после вызова Abort() и некоторой задержки (допустим, мы хотим дать ему время умереть)?

Да, кстати, я где-то читал о том, что использование TerminateThread в будущем возможно приведет к фатальным ошибкам из-за того, что в реализации механизма потоков могут применяться фиберы, на которых TerminateThread, естесственно, не окажет должного воздействия
370
14 ноября 2007 года
koval
443 / / 29.08.2005
Цитата: Mmonstr
Да, видимо это единственный сейчас доступный метод.. Тогда возникает еще 2 вопроса: откуда взять хендл этого потока (в классе TThread не нашел.. может я просто не туда смотрел?) и как определить, жив ли выбранный поток после вызова Abort() и некоторой задержки (допустим, мы хотим дать ему время умереть)?

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



По поводу второго вороса:
Cвойство Thread
IsAlive

713
15 ноября 2007 года
Ap0k
360 / / 13.03.2006
Актуальный вопрос, довльно часто возникает :-)
Вот комментарии гуру по этому поводу: [COLOR=#285a99]http://blogs.msdn.com/junfeng/archive/2004/02/03/66502.aspx[/COLOR]
Смысл в том, что нет прямой зависимости между потоком в управляемой среде и потоком операционной системы. Хост CLR может самомстоятельно, например, объединять несколько потоков в контекст одного физически выполняемого и т.д. все зависит от реализации.
Вот код, сейчас сам играюсь, чтобы для себя прояснить ситуацию. Правда TerminateThread почему-то не завершает поток:
Код:
[SIZE=2][COLOR=#0000ff]class [/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]Program[/COLOR][/SIZE]
[SIZE=2]{[/SIZE]
[SIZE=2][COLOR=#0000ff]   static [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] Main([/SIZE][SIZE=2][COLOR=#0000ff]string[/COLOR][/SIZE][SIZE=2][] args)[/SIZE]
[SIZE=2]   {[/SIZE]
[SIZE=2]      printProcessThreads();[/SIZE]
[SIZE=2][COLOR=#2b91af]      ThreadStart[/COLOR][/SIZE][SIZE=2] unmanagedAsynkWork = [/SIZE][SIZE=2][COLOR=#0000ff]delegate[/COLOR][/SIZE][SIZE=2]()[/SIZE]
[SIZE=2]      {[/SIZE]
[SIZE=2][COLOR=#2b91af]         Console[/COLOR][/SIZE][SIZE=2].Out.WriteLine([/SIZE][SIZE=2][COLOR=#a31515]"th{0}: Listening for clients..."[/COLOR][/SIZE][SIZE=2], [/SIZE][SIZE=2][COLOR=#2b91af]Thread[/COLOR][/SIZE][SIZE=2].CurrentThread.ManagedThreadId);[/SIZE]
[SIZE=2][COLOR=#2b91af]         TcpListener[/COLOR][/SIZE][SIZE=2] listener = [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]TcpListener[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#2b91af]IPAddress[/COLOR][/SIZE][SIZE=2].Any, 1010);[/SIZE]
[SIZE=2]         listener.Start();[/SIZE]
[SIZE=2]         listener.AcceptTcpClient();[/SIZE]
[SIZE=2][COLOR=#2b91af]         Console[/COLOR][/SIZE][SIZE=2].Out.WriteLine([/SIZE][SIZE=2][COLOR=#a31515]"th{0}: Client accepted..."[/COLOR][/SIZE][SIZE=2],[/SIZE][SIZE=2][COLOR=#2b91af]Thread[/COLOR][/SIZE][SIZE=2].CurrentThread.ManagedThreadId);[/SIZE]
[SIZE=2]      };[/SIZE]
[SIZE=2][COLOR=#2b91af]      LongOperation[/COLOR][/SIZE][SIZE=2] operation = [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]LongOperation[/COLOR][/SIZE][SIZE=2](unmanagedAsynkWork);[/SIZE]
[SIZE=2][COLOR=#2b91af]      Console[/COLOR][/SIZE][SIZE=2].Out.WriteLine([/SIZE][SIZE=2][COLOR=#a31515]"th{0}: Starting longop"[/COLOR][/SIZE][SIZE=2],[/SIZE][SIZE=2][COLOR=#2b91af] Thread[/COLOR][/SIZE][SIZE=2].CurrentThread.ManagedThreadId);[/SIZE]
[SIZE=2]      operation.StartWork();[/SIZE]
[SIZE=2][COLOR=#2b91af]      Thread[/COLOR][/SIZE][SIZE=2].Sleep(5000);[/SIZE]
[SIZE=2]      printProcessThreads();[/SIZE]
[SIZE=2][COLOR=#2b91af]      Console[/COLOR][/SIZE][SIZE=2].Out.WriteLine([/SIZE][SIZE=2][COLOR=#a31515]"th{0}: Abort unmanagedAsynkWork"[/COLOR][/SIZE][SIZE=2],[/SIZE][SIZE=2][COLOR=#2b91af]  Thread[/COLOR][/SIZE][SIZE=2].CurrentThread.ManagedThreadId);[/SIZE]
[SIZE=2]      operation.Win32Abort();[/SIZE]
[SIZE=2]      printProcessThreads();[/SIZE]
[SIZE=2][COLOR=#2b91af]      Console[/COLOR][/SIZE][SIZE=2].Write([/SIZE][SIZE=2][COLOR=#a31515]"Work complete! Press Enter to exit.."[/COLOR][/SIZE][SIZE=2]);[/SIZE]
[SIZE=2][COLOR=#2b91af]      Console[/COLOR][/SIZE][SIZE=2].ReadLine();[/SIZE]
[SIZE=2]   }[/SIZE]
[SIZE=2][COLOR=#0000ff]   private [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]static [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] printProcessThreads()[/SIZE]
[SIZE=2]   {[/SIZE]
[SIZE=2][COLOR=#2b91af]      ProcessThreadCollection[/COLOR][/SIZE][SIZE=2] threads = [/SIZE][SIZE=2][COLOR=#2b91af]Process[/COLOR][/SIZE][SIZE=2].GetCurrentProces().Threads;[/SIZE]
[SIZE=2][COLOR=#2b91af]      Console[/COLOR][/SIZE][SIZE=2].WriteLine([/SIZE][SIZE=2][COLOR=#a31515]"Threads in current process:"[/COLOR][/SIZE][SIZE=2]);[/SIZE]
[SIZE=2][COLOR=#0000ff]      foreach[/COLOR][/SIZE][SIZE=2] ([/SIZE][SIZE=2][COLOR=#2b91af]ProcessThread[/COLOR][/SIZE][SIZE=2] thread [/SIZE][SIZE=2][COLOR=#0000ff]in[/COLOR][/SIZE][SIZE=2] threads)[/SIZE]
[SIZE=2]      {[/SIZE]
[SIZE=2][COLOR=#2b91af]          Console[/COLOR][/SIZE][SIZE=2].Out.WriteLine([/SIZE][SIZE=2][COLOR=#a31515]" ID={0,4} (0x{1:X8}) State={2} WaitReason= {3}"[/COLOR][/SIZE][SIZE=2],[/SIZE]
[SIZE=2]          thread.Id, thread.StartAddress.ToInt64(),[/SIZE]
[SIZE=2]          thread.ThreadState,[/SIZE]
[SIZE=2]          (thread.ThreadState == System.Diagnostics.[/SIZE][SIZE=2][COLOR=#2b91af]ThreadState[/COLOR][/SIZE][SIZE=2].Wait)?[/SIZE]
[SIZE=2]          thread.WaitReason.ToString() : [/SIZE][SIZE=2][COLOR=#a31515]"None"[/COLOR][/SIZE][SIZE=2]);[/SIZE]
      [SIZE=2]}[/SIZE]
[SIZE=2]   }[/SIZE]
[SIZE=2]}[/SIZE]
[SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]class [/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]LongOperation[/COLOR][/SIZE]
[SIZE=2]{[/SIZE]
[SIZE=2][COLOR=#0000ff]    #region[/COLOR][/SIZE][SIZE=2] Platform invoke[/SIZE]
[SIZE=2]    [[/SIZE][SIZE=2][COLOR=#2b91af]DllImport[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#a31515]"Kernel32.dll"[/COLOR][/SIZE][SIZE=2], EntryPoint = [/SIZE][SIZE=2][COLOR=#a31515]"GetCurrentThreadId"[/COLOR][/SIZE][SIZE=2], ExactSpelling = [/SIZE][SIZE=2][COLOR=#0000ff]true[/COLOR][/SIZE][SIZE=2])][/SIZE]
[SIZE=2][COLOR=#0000ff]    private [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]static [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]extern [/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]Int32[/COLOR][/SIZE][SIZE=2] GetCurrentWin32ThreadId();[/SIZE]
[SIZE=2]    [[/SIZE][SIZE=2][COLOR=#2b91af]DllImport[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#a31515]"kernel32.dll"[/COLOR][/SIZE][SIZE=2])][/SIZE]
[SIZE=2][COLOR=#0000ff]    private [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]static [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]extern [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]bool[/COLOR][/SIZE][SIZE=2] TerminateThread([/SIZE][SIZE=2][COLOR=#2b91af]Int32[/COLOR][/SIZE][SIZE=2] id, [/SIZE][SIZE=2][COLOR=#2b91af]Int32[/COLOR][/SIZE][SIZE=2] dwexit);[/SIZE]
[SIZE=2][COLOR=#0000ff]    #endregion[/COLOR][/SIZE]
[SIZE=2][COLOR=#0000ff]    private [/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]Thread[/COLOR][/SIZE][SIZE=2] _thread;[/SIZE]
[SIZE=2][COLOR=#0000ff]    private [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2] _win32ThreadID;[/SIZE]
[SIZE=2][COLOR=#0000ff]    private [/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]ThreadStart[/COLOR][/SIZE][SIZE=2] _taskEntryPoint;[/SIZE]
[SIZE=2][COLOR=#0000ff]    public[/COLOR][/SIZE][SIZE=2] LongOperation([/SIZE][SIZE=2][COLOR=#2b91af]ThreadStart[/COLOR][/SIZE][SIZE=2] taskEntryPoint)[/SIZE]
[SIZE=2]    {[/SIZE]
[SIZE=2]       _taskEntryPoint = taskEntryPoint;[/SIZE]
[SIZE=2]       _thread = [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]Thread[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2][COLOR=#2b91af]ThreadStart[/COLOR][/SIZE][SIZE=2](processTask));[/SIZE]
[SIZE=2]       _thread.IsBackground = [/SIZE][SIZE=2][COLOR=#0000ff]true[/COLOR][/SIZE][SIZE=2];[/SIZE]
[SIZE=2]    }[/SIZE]
[SIZE=2][COLOR=#0000ff]    public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] StartWork()[/SIZE]
[SIZE=2]    {[/SIZE]
[SIZE=2]       _thread.Start();[/SIZE]
[SIZE=2]    }[/SIZE]
[SIZE=2][COLOR=#0000ff]    private [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] processTask()[/SIZE]
[SIZE=2]    {[/SIZE]
[SIZE=2]       _win32ThreadID = GetCurrentWin32ThreadId();[/SIZE]
[SIZE=2][COLOR=#0000ff]      try[/COLOR][/SIZE]
[SIZE=2]      {[/SIZE]
[SIZE=2]         _taskEntryPoint();[/SIZE]
[SIZE=2]      }[/SIZE]
[SIZE=2][COLOR=#0000ff]      catch[/COLOR][/SIZE][SIZE=2] ([/SIZE][SIZE=2][COLOR=#2b91af]Exception[/COLOR][/SIZE][SIZE=2] ex)[/SIZE]
[SIZE=2]      {[/SIZE]
[SIZE=2][COLOR=#2b91af]         Console[/COLOR][/SIZE][SIZE=2].Out.WriteLine([/SIZE][SIZE=2][COLOR=#a31515]"th{0}: Exception {1} thrown"[/COLOR][/SIZE][SIZE=2],[/SIZE][SIZE=2][COLOR=#2b91af] Thread[/COLOR][/SIZE][SIZE=2].CurrentThread.ManagedThreadId, ex.GetType());[/SIZE]
[SIZE=2]      }[/SIZE]
[SIZE=2]   }[/SIZE]
[SIZE=2][COLOR=#0000ff]   public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] Win32Abort()[/SIZE]
[SIZE=2]   {[/SIZE]
[SIZE=2]       TerminateThread(_win32ThreadID, 0);[/SIZE]
[SIZE=2]   }[/SIZE]
[SIZE=2]}[/SIZE]


Вывод в консоль:
Цитата:
Код:
Threads in current process:
 ID=5108 (0x7C810685) State=Running WaitReason=None
 ID=5112 (0x7C810679) State=Wait    WaitReason=UserRequest
 ID=5120 (0x7C810679) State=Wait    WaitReason=UserRequest
th1: Starting longop
th3: Listening for clients...
Threads in current process:
 ID=5108 (0x7C810685) State=Running WaitReason=None
 ID=5112 (0x7C810679) State=Wait    WaitReason=UserRequest
 ID=5120 (0x7C810679) State=Wait    WaitReason=UserRequest
 ID=4976 (0x7C810679) State=Wait    WaitReason=UserRequest
th1: Abort unmanagedAsynkWork
Threads in current process:
 ID=5108 (0x7C810685) State=Running WaitReason=None
 ID=5112 (0x7C810679) State=Wait    WaitReason=UserRequest
 ID=5120 (0x7C810679) State=Wait    WaitReason=UserRequest
 ID=4976 (0x7C810679) State=Wait    WaitReason=UserRequest
Work complete! Press Enter to exit..


Т.е. как было 4 потока до вызова, так и осталось :confused:

30K
15 ноября 2007 года
Mmonstr
9 / / 14.09.2007
Вот в том-то и проблема, что опираться на реализацию managed-потоков опасно.. У меня появилась одна мысль по этому поводу, учитывающая особенность, о которой говорил предыдущий оратор. К примеру, если мы можем перебрать в цикле все потоки приложения (используя Win32 API), нам достаточно выбрать тот поток, который имел несчастье зависнуть в unmanaged коде, чтобы вызвать TerminateThread. Значит, нам остается составить внутри программы таблицу с уникальными идентификаторами и сопоставлять значения из нее и хендлы перебираемых потоков, чтобы найти тот поток, который необходимо уничтожить. Если же мы нашли коллизию (т.е. два или более идентификаторов ссылаются на один физический поток - как раз в том случае, когда .NET Framework самостоятельно реализует поток, не прибегая к услугам Win32) - то оставляем этот поток живым, чтобы не убить заодно с ним другие, рабочие. Вопрос в реализации и будет ли такой метод достаточно эффективным?
713
15 ноября 2007 года
Ap0k
360 / / 13.03.2006
Цитата:
..К примеру, если мы можем перебрать в цикле все потоки приложения (используя Win32 API)..


Просмотрите внимательно код printProcessThreads.

Цитата:
..нам остается составить внутри программы таблицу с уникальными идентификаторами и сопоставлять значения из нее и хендлы перебираемых потоков..


Получить явную зависимость между managed-потоком и потоком операционной сестемы нельзя, только если вызывать [COLOR=#a31515]GetCurrentThreadId [/COLOR][COLOR=black]см processTask()[/COLOR]

30K
15 ноября 2007 года
Mmonstr
9 / / 14.09.2007
Цитата: Ap0k
Просмотрите внимательно код printProcessThreads.



Сорри, но вызовов Win32 API я там не увидел. Мне кажется то обстоятельство, что Вы делали перебор потоков именно средствами .NET, и обусловил полученный результат. Т.е. поток искусственно завис, и Вы его прервали функцией TerminateThread. Но ресурсы, которые были выделены средой .NET, не были освобождены, имхо поэтому и перебор дает необъективную картину. Framework думает, что потоки работают, и Ваш код выводит их состояние независимо от того, были ли они прерваны функцией TerminateThread.

713
16 ноября 2007 года
Ap0k
360 / / 13.03.2006
Цитата: Mmonstr
Сорри, но вызовов Win32 API я там не увидел. Мне кажется то обстоятельство, что Вы делали перебор потоков именно средствами .NET, и обусловил полученный результат. Т.е. поток искусственно завис, и Вы его прервали функцией TerminateThread. Но ресурсы, которые были выделены средой .NET, не были освобождены, имхо поэтому и перебор дает необъективную картину. Framework думает, что потоки работают, и Ваш код выводит их состояние независимо от того, были ли они прерваны функцией TerminateThread.


[quote=MSDN]
Use ProcessThread to obtain information about a thread that is currently running on the system. Doing so allows you, for example, to monitor the thread's performance characteristics.
A thread is a path of execution through a program. It is the smallest unit of execution that Win32 schedules. It consists of a stack, the state of the CPU registers, and an entry in the execution list of the system scheduler.
A process consists of one or more threads and the code, data, and other resources of a program in memory. Typical program resources are open files, semaphores, and dynamically allocated memory. Each resource of a process is shared by all that process's threads.
A program executes when the system scheduler gives execution control to one of the program's threads. The scheduler determines which threads should run and when. A lower-priority thread might be forced to wait while higher-priority threads complete their tasks. On multiprocessor computers, the scheduler can move individual threads to different processors, thus balancing the CPU load.
Each process starts with a single thread, which is known as the primary thread. Any thread can create additional threads. All the threads within a process share the address space of that process.
Note: The primary thread is not necessarily located at the first index in the collection.

The threads of a process execute individually and are unaware of each other unless you make them visible to each other. Threads that share common resources, however, must coordinate their work by using semaphores or another method of interprocess communication.[/quote]
Методом printProcessThreads я лишь пытался показать, что не обязательно использовать platform invoke для получения всех потоков. Просьба не путать класс [COLOR=teal]ProcessThread[/COLOR] и [COLOR=teal]Thread[/COLOR]. В частности свойство [COLOR=teal]ProcessThread[/COLOR].Id не является аналогом [COLOR=teal]Thread[/COLOR].ManagedThreadId, и возращает оно, тоже самое, что и вызов Win32 API функции [COLOR=black]GetCurrentThreadId в контексте выполняемого потока. [/COLOR]
Напрямую перейти от экземпляра одного класса ([COLOR=teal]Thread[/COLOR]) к экземпляру другого ([COLOR=teal]ProcessThread[/COLOR]) нет возможности, кроме как получить в контексте потока который нужно "прибить" его идентификатор, но это уже как раз та алхимия и её реализация, которой вы говорили в одном из своих постов...:

 
Код:
[COLOR=#0000ff] private void[/COLOR] processTask()
 {
    _win32ThreadID = GetCurrentWin32ThreadId();
       [COLOR=green]//тут уже можно работать, висеть, что угодно..[/COLOR]
    }

Раньше был метод [COLOR=teal]AppDomain[/COLOR].GetCurrentThreadId(), который теперь deprecated
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог