Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: _beginthreadex из библиотеки Visual C++?  (Прочитано 24959 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« : 06-06-2007 07:29 » 

В своей статье про потоки в Win32 (лежит в разделе real-time) Гром написал следующее:
"CreateThread - это Windows-функция, создающая поток. Если вы пишете код на С/С++, не вызывайте ее. Вместо нее вы должны использовать _beginthreadex из библиотеки Visual C++."

в связи с чем меня интересует - ПОЧЕМУ? раз продолжения статьи вроде нет...
почему нельзя использовать родную функцию?
единственная разница которую я уловил это то, что
CreateThread требует ___stdcall функции нити
_beginthread  -  __cdecl,
а beginthreadex опять __stdcall.

так почему такое категоричное утверждение?
нафига эти заморочки? чтоб потом под линуксом скомпилить? Не надо

Статья: https://club.shelek.ru/viewart.php?id=71
« Последнее редактирование: 15-02-2008 12:47 от Вад » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
nikedeforest
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 06-06-2007 08:06 » 

Позволю себе цитнуть Рихтера, потому как лучше него не объяснить мне
Цитата
Наверное, Вам уже хочется спросить: "А зачем мне отдельные библиотеки для однопоточных и многопоточных программ?" Отвечаю. Стандартная библиотека С была разработана где-то в 1970 году — задолго до появления самого понятия многопоточности. Авторы этой библиотеки, само собой, не задумывались о проблемах, связанных с многопоточными приложениями.

Возьмем, к примеру, глобальную переменную errno из стандартной библиотеки С. Некоторые функции, если происходит какая-нибудь ошибка, записывают в эту переменную соответствующий код. Допустим, у Вас есть такой фрагмент кода:
Код:
BOOL fFailure = (system("NOTEPAD.EXE README.TXT") == -1); 

if (fFailure)
{
switch (errno)
{
case E2BIG:
// список аргументов или размер окружения слишком велик
break;

case ENOENT:
// командный интерпретатор не найден
break;

case ENOEXEC;
// неверный формат командного интерпретатора
break;

case ENOMEM:
// недостаточно памяти для выполнения команды
break;
}
Цитата
Теперь представим, что поток, выполняющий показанный выше код, прерван после вызова функции system и до оператора if. Допустим также, поток прерван для выполнения другого потока (в том же процессе), который обращается к одной из функций библиотеки С, и та тоже заиосит какое то значение в глобальную переменную errno. Смотрите, что получается когда процессор вернется к выполнению первого потока, в переменной errno окажется вовсе не то значение, которое было записано функцией system. Поэтому для решения этой проблемы нужно закрепить за каждым потоком свою переменную errno. Кроме того, понадобится какой-то механизм, который позволит каждому потоку ссылаться на свою переменную errno и не трогать чужую.

Это лишь один пример того, что стандартная библиотека С/С++ не рассчитана на многопоточные приложения. Кроме errno, в ней есть еще целый ряд переменных и функций, с которыми возможны проблемы в многопоточной среде _doserrno, strtok, _wcstok, strerror, _strerror, tmpnam, tmpfile, a<tcttme, _wascttme, gmttme, _ecvt, _Jcvt - список можно продолжить.

Чтобы многопоточные программы, использующие библиотеку С/С++, работали корректно, требуется создать специальную структуру данных и связать ее с каждым потоком, из которого вызываются библиотечные функции. Более того, они должны знать, что, когда Вы к ним обращаетесь, нужно просматривать этот блок данных в вызывающем потоке чтобы не повредить данные в каком-нибудь другом потоке.

Так откуда же система знает, что при создании нового потока надо создать и этот блок данных3. Ответ очень прост - не знает и знать не хочет. Вся ответственность — исключительно на Вас. Если Вы пользуетесь небезопасными в многопоточной среде функциями, то должны создавать потоки библиотечной функцией _begmthreadex, а не Windows-функцией CreateThread.


Записан

ещё один вопрос ...
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #2 : 06-06-2007 09:00 » 

На самомо деле всё гораздо проще. Касательно вопроса про треды. Обычно под виндой когда начинаешь работать с тредами читаешь MSDN ну и это вроде прописной истины, поэтому Гром наверное и не стал распространяться. Времени нет объяснять, думаю с аглицким проблем нет?

Цитата: MSDN
All C Run-time functions except the signal() function work correctly when used in threads created by the CreateThread() function. However, depending on what CRT functions are called, there may be a small memory leak when threads are terminated. Calling strlen(), for example, does not trigger the allocation of the CRT thread data-block, and calling malloc(), fopen(), _open(), strtok(), ctime(), or localtime() causes allocation of a CRT per-thread data-block, which may cause a memory leak.
MORE INFORMATION
The "Programming Techniques" manual supplied with Visual C++ 32-bit Edition states that using CreateThread() in a program that uses Libcmt.lib causes many CRT functions to fail. Actually, the only function that should not be used in a thread created with CreateThread() is the signal() function.

There are two ways to create threads. One method involves using the CRT _beginthread() or _beginthreadex() (with Visual C++ 2.0 and later); the other method involves using the CreateThread() API. All CRT functions other than the signal() function work correctly in threads created with either _beginthread() or CreateThread(). However, there are some problems involved with using CRT functions in threads created with CreateThread().

Threads that are created and terminated with the CreateThread() and ExitThread() Win32 API functions do not have memory that is allocated by the CRT for static data and static buffers cleaned up when the thread terminates. Some examples of this type of memory are static data for errno and _doserrno and the static buffers used by functions such as asctime(), ctime(), localtime(), gmtime(), and mktime(). Using CreateThread() in a program that uses the CRT (for example, links with LIBCMT.LIB) may cause a memory leak of about 70-80 bytes each time a thread is terminated.

To guarantee that all static data and static buffers allocated by the CRT are cleaned up when the thread terminates, _beginthreadex() and _endthreadex() should be used when creating a thread. The _beginthreadex() function includes the same parameters and functionality as CreateThread().

NOTE: It is not possible to terminate a thread with _endthreadex() when it was created with CreateThread().
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
nikedeforest
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 06-06-2007 09:10 » 

Цитата
думаю с аглицким проблем нет?
Да есть, к сожалению, но куда деваться.
Записан

ещё один вопрос ...
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #4 : 06-06-2007 09:18 » 

Да есть, к сожалению, но куда деваться.

Ок, тогда вечерком попробую перевести в доступную форму, если буду в состоянии стояния. Ага
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
nikedeforest
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 06-06-2007 09:33 » 

Да ну, главный смысл я вроде уловил. Так что не стоит. А вот мозгов не хватило в другом. Понял я что будут маленькие утечки в 70-80 байт пр каждом завершении. И как бы объяснение причины, типа функция CreateThread  не имеет своей памяти, а функции CRT могут иметь статические данные и тиап из-за этого утечки и возникают. НО так я и не понял из-за чего конкретно. Т.е. имелось ввиду, что раз CreateThread не имеет своей памяти, то ExitThread ничего не осовбождает? Или в том плане, что если бы функция CreateThread имела бы свою память, то CRT-функции могли бы ее использовать, а при вызове ExitThread это все махом освободилось? Что имелось конкретно ввиду?
И к чему тогда это повисшее в воздухе замечание, что, если уж вы вызвали CreateThread, то завершайте поток ExitThread, а не _endthreadex. Зачем это замечане? А я блин возьму и не вызову ExitThread, а просто сзделаю функцией return и что тогда? Лучше бы это расписали.
« Последнее редактирование: 06-06-2007 09:37 от nikedeforest » Записан

ещё один вопрос ...
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 06-06-2007 09:37 » 

Джон,
о! теперь совсем понятно) хотя с английским тоже беда)))
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 06-06-2007 09:42 » 

nikedeforest,
CreateThread - создает нить (со своим стеком), и когда вы вызваете в нем указанные функции, они не находят служебных переменных , и я так понял сами создают их при первом вызове? (поправте если не прав, или наоборот не создают - поэтому будет ошибка?). при выходе из нити - ExitThread не в курсе о размещенной памяти, поэтому память не высвобождается?
а beginthread кроме создания нити размещает в памяти процесса служебные переменные этой нити, которые используются разными функциями.
ExitThread - API она об этих переменных ничего не знает.

тфу.ExitThread это вообще равносильно return; из нити. CloseHandle я имел в виду.
PS короче
beginthread - размещает служебную память переменных и dspsdftn CreateThread
ExitThread(err); и return err; - окончание работы нити (вызывается из нити)
endthreadex - вызывает CloseHandle() и освобождает память выделенную под служебные переменные нити. (вызывается из нити)
« Последнее редактирование: 06-06-2007 09:54 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
nikedeforest
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 07-06-2007 21:27 » 

Как вижу, дополнить никто больше не желает и споров ни у кого ничего не вызвало, так что принимаю последнее за истину. Благодарю за разъяснение.
Записан

ещё один вопрос ...
sss
Специалист

ru
Offline Offline

« Ответ #9 : 08-06-2007 02:34 » 

А я думаю, что применение beginthreadex заставляет использовать стандартные библиотеки для многопоточной версии. В них функция возвращает буфер для вызвавшего ее потока. В однопоточном исполнении буфер соответственно один и все потоки будут использовать его.
« Последнее редактирование: 09-06-2007 00:36 от sss » Записан

while (8==8)
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 09-06-2007 14:56 » 

sss,
... простите, какой буфер???
PS какие библиотеки использовать - указываете вы, в настройках компилятора)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 15-06-2007 08:03 » new

Досточно посмотреть на код функции _endthread (MS VC 2005)

Код:
void __cdecl _endthread ( void  )
{
        _ptiddata ptd;           /* pointer to thread's _tiddata struct */
        /*
         * Call fp termination, if necessary
         */
#ifdef CRTDLL
        _fpclear();
#else  /* CRTDLL */
        if (_FPmtterm != NULL &&
            _IsNonwritableInCurrentImage((PBYTE)&_FPmtterm))
        {
            (*_FPmtterm)();
        }
#endif  /* CRTDLL */
        ptd = _getptd_noexit();
        if (ptd) {
            /*
             * Close the thread handle (if there was one)
             */
            if ( ptd->_thandle != (uintptr_t)(-1) )
                    (void) CloseHandle( (HANDLE)(ptd->_thandle) );
            /*
             * Free up the _tiddata structure & its subordinate buffers
             *      _freeptd() will also clear the value for this thread
             *      of the FLS variable __flsindex.
             */
            _freeptd(ptd);
        }
        /*
         * Terminate the thread
         */
        ExitThread(0);
}

Видно, что перед тем, как завершить поток, функция освобождает ресурсы, связанные с функциями библиотеки языка Си.  Какие именно данные - см. заголовочный файл mtdll.h. В частности, с каждым потоком связывается значение errno, буферы для функций strerror, strtok, tmpfile, asctime, переменные для обработки исключений С++. Всё это и нужно освободить перед завершением потока.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines