Легенда:
новое сообщение
закрытая нитка
новое сообщение
в закрытой нитке
старое сообщение
|
- Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
- Новичкам также крайне полезно ознакомиться с данным документом.
| | |
именно тем что это обертка над... 03.05.06 16:54 Число просмотров: 3026
Автор: Killer{R} <Dmitry> Статус: Elderman
|
Она дает знать CRT о том что тред запускается/завершается. Если ты не юзаешь CRT'шные функции в треде (что маловероятно), то можно и CreateThread заюзать.
|
<programming>
|
[Win32] Не создается поток 30.04.06 14:58 [Killer{R}]
Автор: makeworld Статус: Member
|
есть такой код. CreateThread() отрабатывает нормально, но признаков работы потока нет. использую VC++ 2005. не могу понять, в чем дело..
#include <windows.h>
#include <stdio.h>
void func();
DWORD WINAPI ThreadProc(LPVOID lParam);
int main() {
func();
return 0;
}
void func() {
unsigned int tid;
char test[30]={0};
memset(&test[0], 'A', 29);
printf("%s\n", test);
HANDLE hT1 = CreateThread(NULL, 0, ThreadProc, &test[0], 0, (LPDWORD)&tid);
HANDLE hT2 = CreateThread(NULL, 0, ThreadProc, &test[0], 0, (LPDWORD)&tid);
HANDLE hT3 = CreateThread(NULL, 0, ThreadProc, &test[0], 0, (LPDWORD)&tid);
if ((hT1 == INVALID_HANDLE_VALUE)| (hT2 == INVALID_HANDLE_VALUE)| (hT3 == INVALID_HANDLE_VALUE))
printf("failed\n");
int lastErr = GetLastError();
if (lastErr)
printf("last error = %d\n", lastErr);
}
DWORD WINAPI ThreadProc(LPVOID lParam) {
char *p = (char *)lParam;
// Sleep(100);
printf("i'm thread %d\n", GetCurrentThreadId());
printf("%s\n", p);
MessageBox(NULL, TEXT("test"), TEXT("test"), MB_OK);
return 0;
}
---
|
|
я бы использовал _beginthreadex. 03.05.06 05:09
Автор: void <Grebnev Valery> Статус: Elderman
|
я бы использовал _beginthreadex.
Со слипами я бы разобрался. Может не нады они? Регулировать переключение слипами не стал бы. Не угадаешь. Если уж надо чего-то ждать - используйте события.
|
| |
а чем _beginthreadex реально лучше? вроде же это обертка над... 03.05.06 16:34
Автор: makeworld Статус: Member
|
> я бы использовал _beginthreadex. > Со слипами я бы разобрался. Может не нады они? Регулировать > переключение слипами не стал бы. Не угадаешь. Если уж надо > чего-то ждать - используйте события.
а чем _beginthreadex реально лучше? вроде же это обертка над API функцией CreateThread
про синхронизацию это само собой
я просто не знал о том, что потоки завершаются автоматом при завершении главного потока
|
| | |
Это не ко мне. Это к Рихтеру. 04.05.06 05:24
Автор: void <Grebnev Valery> Статус: Elderman
|
|
| | |
В который раз замечаю, что меня не замечают (sic!) 03.05.06 17:24
Автор: amirul <Serge> Статус: The Elderman
|
- Доктор, меня игнорируют.
- Следующий
> я просто не знал о том, что потоки завершаются автоматом > при завершении главного потока
К винде это не имеет никакого отношения. Просто в C программа завершается при выходе из функции main. При его разработке никто не закладывался на многопоточность. Вот и приходится при выходе из main прибивать все "лишние" потоки. Если ты поставишь в настройках линкера точку входа, отличную от xxxCRTStartup (вместо xxx - всякие main, wmain, WinMain и wWinMain), то процесс у тебя не завершится (естественно большинством рантаймовых функций ты пользоваться не сможешь и аргументы argv/argc не получшь)
dl уже даже протестил:
http://bugtraq.ru/cgi-bin/forum.mcgi?type=sb&b=2&m=134498
|
| | | |
а вообще этим могла бы заниматься и система 03.05.06 19:46
Автор: dl <Dmitry Leonov>
|
Хэндл главного потока ей по крайней мере при создании процесса известен, могла бы и запомнить, после чего обработать так же, как рантайм.
|
| | | | |
Могла бы, но незачем 03.05.06 21:15
Автор: amirul <Serge> Статус: The Elderman
|
> Хэндл главного потока ей по крайней мере при создании > процесса известен, могла бы и запомнить, после чего > обработать так же, как рантайм.
Не вводи сущностей сверх необходимости и прочее.
А вообще красивый и управляемый код чаще всего имеет высокую степень обобщенности и симметрии. В том смысле, что если нет ОБЯЗАТЕЛЬНЫХ причин выделять какой либо один поток, то лучше не делать этого из эстетических соображений.
А рантайм занимается этим насколько я понимаю именно из за того, что стандарт C напрямую указывает завершать программу (независимо от того, в каком виде она представлена) при выходе из main. Насколько я помню в posix-е и потоков то нету. Один процесс - один поток исполнения (другое дело, что они могут разделять память, хендлы и пр.., но у каждого свой PID и такое прочее).
|
| | | | | |
эстетика дело такое 03.05.06 21:41
Автор: dl <Dmitry Leonov>
|
зато не нужно было бы таскать из программы в программу один и тот же код :)
|
| | | |
пост читал 03.05.06 17:31
Автор: makeworld Статус: Member
|
> К винде это не имеет никакого отношения. Просто в C > программа завершается при выходе из функции main. При его > разработке никто не закладывался на многопоточность. Вот и > приходится при выходе из main прибивать все "лишние" > потоки. Если ты поставишь в настройках линкера точку входа, > отличную от xxxCRTStartup (вместо xxx - всякие main, wmain, > WinMain и wWinMain), то процесс у тебя не завершится > (естественно большинством рантаймовых функций ты > пользоваться не сможешь и аргументы argv/argc не получшь) > > dl уже даже протестил: > http://bugtraq.ru/cgi-bin/forum.mcgi?type=sb&b=2&m= > 134498
пост читал
теперь понятно, что дело в CRT, хотя в принципе, если создавать обычное приложение без изменения точки входа, откючения CRT и т.д., не важно на уровне CRT или на уровне ОС происходит принудительное завершение потоков, результат один и тот же.
|
| | | | |
[UPD]как сказать 03.05.06 17:36
Автор: Killer{R} <Dmitry> Статус: Elderman Отредактировано 03.05.06 17:46 Количество правок: 2
|
> пост читал > теперь понятно, что дело в CRT, хотя в принципе, если > создавать обычное приложение без изменения точки входа, > откючения CRT и т.д., не важно на уровне CRT или на уровне > ОС происходит принудительное завершение потоков, результат > один и тот же. как сказать
#include "windows.h"
DWORD CALLBACK trd(void *)
{
for(;;)Sleep(1000);
}
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
CreateThread(NULL,0,trd,NULL,0,NULL);
ExitThread(0);
return 0;
}
[UPD]
BTW у меня есть сильное подозрение что когда у процесса заканчиваюцца потоки умирает он тоже не по собственному желанию а по приказу csrss.exe
|
| | | | | |
Нет. Процесс убивается в ядре 03.05.06 17:59
Автор: amirul <Serge> Статус: The Elderman
|
> BTW у меня есть сильное подозрение что когда у процесса > заканчиваюцца потоки умирает он тоже не по собственному > желанию а по приказу csrss.exe
DECLSPEC_NORETURN
VOID
PspExitThread(
IN NTSTATUS ExitStatus
)
{
// [skipped]
Thread = PsGetCurrentThread();
Process = THREAD_TO_PROCESS(Thread);
// [skipped]
if ( (Process->ThreadListHead.Flink == Process->ThreadListHead.Blink)
&& (Process->ThreadListHead.Flink == &Thread->ThreadListEntry) ) {
LastThread = TRUE;
if ( ExitStatus == STATUS_THREAD_IS_TERMINATING ) {
if ( Process->ExitStatus == STATUS_PENDING ) {
Process->ExitStatus = Process->LastThreadExitStatus;
}
} else {
Process->ExitStatus = ExitStatus;
}
DbgkExitProcess(ExitStatus);
}
// [skipped]
if ( LastThread && Process->Win32Process ) {
(PspW32ProcessCallout)(Process,FALSE); // Вот тут он разрегистрируется в csrss-е
}
// [skipped]
//
// If this is the last thread in the process, then clean the address space
//
if ( LastThread ) {
if (!(ExChangeHandle(PspCidTable,Process->UniqueProcessId, PspMarkCidInvalid, PSP_INVALID_ID))) {
KeBugCheck(CID_HANDLE_DELETION);
}
KeQuerySystemTime(&Process->ExitTime);
PspExitProcess(TRUE,Process);
}
// [skipped]
}
---
Такие дела.
Да, все функции убивания потока заканчиваются в этой самой PspExitThread. Если TerminateThread вызван для текущего потока, то напрямую, а если для другого - через special APC
|
| | | | | |
ну я имел ввиду относительно моего примера 03.05.06 17:49
Автор: makeworld Статус: Member Отредактировано 03.05.06 17:53 Количество правок: 4
|
> #include "windows.h" > > DWORD CALLBACK trd(void *) > { > for(;;)Sleep(1000); > } > int WINAPI WinMain( > HINSTANCE hInstance, // handle to current instance > HINSTANCE hPrevInstance, // handle to previous instance > LPSTR lpCmdLine, // command line > int nCmdShow // show state > ) > { > CreateThread(NULL,0,trd,NULL,0,NULL); > ExitThread(0); > return 0; > }
ну я имел ввиду относительно моего примера. А так вообще конечно надежней было бы, если бы этим ОС занималась. Тут вообще конфликт получается. С одной стороны есть CRT, а с другой вполне легальная функция ExitThread(), которой до CRT и ее функций, вызываюшихся после выхода из main/WinMain и т.д., дела нет.
|
| | |
именно тем что это обертка над... 03.05.06 16:54
Автор: Killer{R} <Dmitry> Статус: Elderman
|
Она дает знать CRT о том что тред запускается/завершается. Если ты не юзаешь CRT'шные функции в треде (что маловероятно), то можно и CreateThread заюзать.
|
| | | |
т.е. CRT-шные функции, используемые в потоке, будут работать... 03.05.06 17:26
Автор: makeworld Статус: Member
|
> Она дает знать CRT о том что тред запускается/завершается.
т.е. CRT-шные функции, используемые в потоке, будут работать учитывая то, что выполняются внутри потока??
можно простой пример пользы от примения _beginthreadex
|
| | | | |
примерно так 03.05.06 19:43
Автор: dl <Dmitry Leonov>
|
Точнее, для глобальных данных, используемых этими функциями, будет сделана обертка, привязанная к конкретному потоку, так чтобы их совместное использование из разных потоков не привело к глюкам.
Хотя на самом деле все не так страшно и может даже проскочить:
"Ой, вместо _beginthreadex я по ошибке вызвал CreateThread
Вас, наверное, интересует, что случится, если создать поток не библиотечной функ цией _begintbreadex, а Windows-функцией CreateThread Когда этот поток вызовет какую-нибудь библиотечную функцию, которая манипулирует со структурой tiddata, произойдет следующее. (Большинство библиотечных функций реентерабсльно и не требует этой структуры ) Сначала эта функция попытается выяснить адрес блока дан ных потока (вызовом TleGetValue). Получив NULL вместо адреса tiddata, она узнает, что вызывающий поток не сопоставлен с таким блоком. Тогда библиотечная функция тут
же создаст и инициализирует блок tiddata для вызывающего потока. Далее этот блок будет сопоставлен с потоком (через TlsSetValue) и останется при нем до тех пор, пока выполнение потока нс прекратится, С этого моменга данная функция (как, впрочем, и любая другая из библиотеки С/С++) сможет пользоваться блоком tiddata потока.
Как это ни фантасгично, но Ваш поток будет работать почти без глюков. Хотя некоторые проблемы все же появятся. Во-первых, если этот поток воспользуется биб лиотечной функцией signal, весь процесс завершится, так как SEH-фрейм не подго товлен. Во-вторых, если поток завершится, не вызвав endtbreadex, его блокданных не высвободится и произойдет утечка памяти. (Да и кто, интересно, вызовет end threadex иэ потока, созданного с помощью CreateTbread?)".
|
| | | | | |
ясно, спасибо. 03.05.06 20:03
Автор: makeworld Статус: Member
|
ясно, спасибо.
хотя signal не самая часто используемая функция, повод для использования именно CRT-обертки создания потока существенный
|
| | | | | | |
есть такие понятия как documentation и implementation 03.05.06 20:33
Автор: Killer{R} <Dmitry> Статус: Elderman
|
так вот если в документации написано чтотоНАДОделать так, а на практике известно что в принципе работает и по другому писать надо все равно именноТАКкак написано. Тк имплементация данной функции может поменяться как угодно любым хотфиксом, функция будет работать так как написано в документации, но не так как работала в прошлой версии CRT. В результате - юзера ругают программистов, пишут в техсуппорт, техсуппорт отправляет это дело манагерам, манагеры - ковыряют кто написал код который только делает вид что работает и увольняют его. Вот это основная причина почему надо юзать _beginthreadex вместо CreateThread, а не особенность поведения signal в внештатной ситуации в конкретной имплементации CRT.
|
| | | | |
Есть такое слово 03.05.06 17:31
Автор: Killer{R} <Dmitry> Статус: Elderman Отредактировано 03.05.06 17:33 Количество правок: 1
|
надо
Что и как там работает - тебя вообще в данном случае не должно интересовать, и если в конкретной версии CRT и твоих исходников CreateThread работает нормально это не значит что всегда так будет.
http://www.flounder.com/badprogram.htm
|
| | | | | |
Кстати, в VC 2005 нет single-threaded libraries, есть только... 03.05.06 19:56
Автор: makeworld Статус: Member
|
> надо > Что и как там работает - тебя вообще в данном случае не > должно интересовать, и если в конкретной версии CRT и твоих > исходников CreateThread работает нормально это не значит > что всегда так будет.
Кстати, в VC 2005 нет single-threaded libraries, есть только multi-threaded, и по дефолту для Debug-сборки стоит Multi-threaded Debug DLL, а для Release - Multi-threaded DLL. Т.е. главные довод, приведенный в линке, отбрасывается.
Хотя в принципе, ради избежания гемора с CRT функциями в потоках в будущем, лучше наверное использовать _beginthreadex.
CreateThread in a C program
|
|
и будет тебе счастье :) 30.04.06 16:10
Автор: ggg <ggg> Статус: Elderman
|
и будет тебе счастье :)
> > int main() { > func(); !!!!!!!!! Sleep(5000);
> return 0; > } >
правда счастье будет длиться 5 секунд :)
|
|
|