Как прибить зависший поток?
Если в момент вызова метода Abort поток выполняет управляемый код, то внутри него будет порождено исключение ThreadAbort и он корректно завершится. В случае, когда поток выполняет неуправляемый код, исключение ThreadAbort будет порождено только после выхода из неуправлемой секции.
Во втором случае завершить поток можно только вызовом Win32 API функции
Да, кстати, я где-то читал о том, что использование TerminateThread в будущем возможно приведет к фатальным ошибкам из-за того, что в реализации механизма потоков могут применяться фиберы, на которых TerminateThread, естесственно, не окажет должного воздействия
Да, кстати, я где-то читал о том, что использование TerminateThread в будущем возможно приведет к фатальным ошибкам из-за того, что в реализации механизма потоков могут применяться фиберы, на которых TerminateThread, естесственно, не окажет должного воздействия
По поводу второго вороса:
Cвойство Thread
IsAlive
Вот комментарии гуру по этому поводу: [COLOR=#285a99]http://blogs.msdn.com/junfeng/archive/2004/02/03/66502.aspx[/COLOR]
Смысл в том, что нет прямой зависимости между потоком в управляемой среде и потоком операционной системы. Хост CLR может самомстоятельно, например, объединять несколько потоков в контекст одного физически выполняемого и т.д. все зависит от реализации.
Вот код, сейчас сам играюсь, чтобы для себя прояснить ситуацию. Правда TerminateThread почему-то не завершает поток:
[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]
Вывод в консоль:
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:
Просмотрите внимательно код printProcessThreads.
Получить явную зависимость между managed-потоком и потоком операционной сестемы нельзя, только если вызывать [COLOR=#a31515]GetCurrentThreadId [/COLOR][COLOR=black]см processTask()[/COLOR]
Сорри, но вызовов 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]) нет возможности, кроме как получить в контексте потока который нужно "прибить" его идентификатор, но это уже как раз та алхимия и её реализация, которой вы говорили в одном из своих постов...:
{
_win32ThreadID = GetCurrentWin32ThreadId();
[COLOR=green]//тут уже можно работать, висеть, что угодно..[/COLOR]
}
Раньше был метод [COLOR=teal]AppDomain[/COLOR].GetCurrentThreadId(), который теперь deprecated