Темы не работают, почему?

Я написал простое тестовое приложение, чтобы доказать, что потоки работают:

    // Test.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"

    class clsTest {
    private:
        uintptr_t muintHandle;

        static unsigned int __stdcall fnThread(void* pData) {
            while( 1 ) {
                _sleep(1000);
                printf("In fnThread, handle = %d\n", *(uintptr_t*)pData);
            }
            return 0;
        }
    public:
        clsTest() {
            muintHandle = _beginthreadex(0, 0, &clsTest::fnThread, (void*)&muintHandle, 0, 0);
            printf("clsTest(), after beginthreadex, handle = %u\n", muintHandle);
        }
    };

    int _tmain(int argc, _TCHAR* argv[]) {
        clsTest* pT = NULL;

        while(1) {
            printf("From _tmain\n");

            if ( pT == NULL ) {
                pT = new clsTest();
            }
            _sleep(1000);
        }
        return 0;
    }

Выход из этого приложения:

    From _tmain
    clsTest(), after beginthreadex, handle = 112
    In fnThread, handle = 112
    From _tmain
    In fnThread, handle = 112
    From _tmain
    In fnThread, handle = 112
    From _tmain
    In fnThread, handle = 112
    From _tmain
    In fnThread, handle = 112
    From _tmain
    In fnThread, handle = 112        
    ...

Непрерывно, именно это я и ожидал увидеть... Теперь в гораздо большем проекте у меня есть базовый класс:

    typedef enum {
            eIdle = 0,      //Thread is not working at all
            eStarted,       //Thread has been started but is not fully operational yet
            eRunning,       //Thread is working normally
            ePausing,       //Thread is requested to enter the paused state
            ePaused,        //Thread is paused
            eTerminating    //Termination has been requested but not completed yet
        } eThreadStates; 

    class clsOpenLDVthread {
    protected:
        volatile eThreadStates meState;
        CRITICAL_SECTION mCritControl;  // critical section for thread control
        char mszName[80];
        HANDLE mhEvent, mhThread;
        virtual bool blnStart() = 0;

    public:
        clsOpenLDVthread(LPCSTR pszName);
        ~clsOpenLDVthread();

        bool inline blnIsRunning();
        bool inline blnIsStopped();
        bool inline blnIsStopping();
        bool inline blnIsStarting();
        bool inline blnIsPausing();
        bool inline blnIsPaused();
        bool blnPause(bool blnState);
        virtual bool blnStop();
    };

    clsOpenLDVthread::clsOpenLDVthread(LPCSTR pszName) : meState(eIdle)
                                               , mhThread(NULL) {
        ::InitializeCriticalSection(&mCritControl); //Get a critical section
        //Get a unique name for signaling event
        sprintf(mszName, "%s%d", pszName, ::GetCurrentProcessId());
        //Get the event object
        mhEvent = ::CreateEvent(NULL, FALSE, FALSE, mszName);
    }       
    clsOpenLDVthread::~clsOpenLDVthread() {
        if ( blnIsPaused() ) {
            blnPause(false);
        }
        if ( blnIsRunning() ) {
            blnStop();
        }
        if ( mhEvent ) {
            ::CloseHandle(mhEvent);
            mhEvent = NULL;
        }
        ::DeleteCriticalSection(&mCritControl);
    }
    bool clsOpenLDVthread::blnIsPaused() {
        return meState == ePaused;
    }
    bool clsOpenLDVthread::blnIsPausing() {
        return meState == ePausing;
    }
    bool clsOpenLDVthread::blnIsRunning() {
        return meState == eRunning;
    }
    bool clsOpenLDVthread::blnIsStarting() {
        return meState == eStarted;
    }
    bool clsOpenLDVthread::blnIsStopped() {
        return meState == eIdle;
    }
    bool clsOpenLDVthread::blnIsStopping() {
        return meState == eTerminating;
    }
    bool clsOpenLDVthread::blnPause(bool blnState) {
        bool blnResult = mhThread != NULL;
        if ( blnResult ) {
            if ( blnState ) {
                unsigned uintCountDown = 10u;

                if ( blnIsRunning() || blnIsPausing() ) {
                    meState = ePausing;
                    while( blnIsPausing() && -- uintCountDown ) {
                        ::SetEvent(mhEvent);
        //Give thread chance to run and pause
                        _sleep(751);
                    }
                    blnResult = blnIsPaused();
                }
            } else {
                if ( blnIsPaused() ) {
                    meState = eRunning;
                    //this will need replacing...mhThread->ResumeThread();
                }
                blnResult = true;
            }
        }
        return blnResult;
    }
    bool clsOpenLDVthread::blnStop() {
        bool blnResult = meState == eIdle;
        unsigned uintCountDown = 100u;

        if ( blnIsPaused() ) {
            blnPause(false);
        }
        if ( blnIsRunning() ) {
            meState = eTerminating;

            while( !blnIsStopped() && --uintCountDown ) {
                if ( mhEvent ) {
                    ::SetEvent(mhEvent);
                }
        //Give thread a change to run and terminate
                _sleep(501);
            }
            blnResult = blnIsStopped();
            mhThread = NULL;
        }
        return blnResult;
    }

Наконец, производный класс, который реализует класс потока и предоставляет метод blnStart:

    class clsOpenLDVrdr : public clsOpenLDVthread {
    public:
    //Maximum size of uplink data per single transfer
        static const unsigned mscuBuffersize;
    private:
    //The thread's main routine
        static void msgReaderThread(LPVOID lpParam);

    public:
        clsOpenLDVrdr();
        virtual ~clsOpenLDVrdr();
    //Call this to start the thread, see clsOpenLDVthread for more operations
        virtual bool blnStart();
    };

    const unsigned clsOpenLDVrdr::mscuBuffersize = MAX_OPENLDV_DATA;

    clsOpenLDVrdr::clsOpenLDVrdr() : clsOpenLDVthread(_T("EvOpenLDVrdr")) {
    }
    clsOpenLDVrdr::~clsOpenLDVrdr() {
    }
    bool clsOpenLDVrdr::blnStart() {
        bool blnResult = false;
        if ( blnIsStopped() ) {
            meState = eStarted;
        //Create the thread
            mhThread = (HANDLE)_beginthread(&clsOpenLDVrdr::msgReaderThread
                                            ,0, NULL);
            blnResult = mhThread != NULL;

            while( blnResult && (meState == eStarted) ) {
        //Give the thread chance to start and initialize
                _sleep(501);
            }
        }
        return blnResult && (meState == eRunning);
    }
    void clsOpenLDVrdr::msgReaderThread(LPVOID lpParam) {
            OutputDebugString("msgReaderThread\n");
    }

Создается экземпляр класса clsOpenLDVrdr и вызывается метод blnStart:

    clsOpenLDVrdr* pobjReader = new clsOpenLDVrdr();
    pobjReader->blnStart();

В отладчике я вижу, что вызывается "blnStart", и заходит в него, все выполняется… но поток никогда не запускается.

Также попытался использовать _beginthreadex вместо _beginthread:

    mhThread = (HANDLE)_beginthreadex(0, 0, pfnThread, pobParam, 0, 0);

Нет разницы. Здесь есть какая-то проблема несовместимости, так как простой пример, который я создал в начале этого поста, работает, и между этими двумя версиями нет большой разницы. За исключением, может быть, способа его использования... первый простой пример был создан как консольное приложение Windows. Проект, с которым я испытываю трудности, находится в DLL.

Я присоединяюсь к DLL с помощью отладчика и перебираю код, который работает до тех пор, пока он не попадет в цикл после вызова beginthread, затем он просто зацикливается навсегда и никогда не попадает в поток.

Я только что попробовал следующее, добавив отдельный поток со стандартной функцией C:

    unsigned __stdcall threadTest(void* pobjData) {
        OutputDebugString("threadTest\n");
        return 0;
   }

Затем я модифицирую вызов "_beginthread" следующим образом:

    mhThread = (HANDLE)_beginthreadex(0, 0, threadTest, pobjParam, 0, 0);

К сожалению, результат тот же, функция threadTest не вызывается. Но верный дескриптор возвращается.

Нашел это:

невозможно вызвать поток в файл DLL

Выглядит интересно и может объяснить странное поведение, которое я испытываю.

1 ответ

Решение

Решено... Сначала я не осознавал, но по какой-то причине существующая DLL вызывала:

    DisableThreadLibraryCalls(hInstance);

Это предотвращает запуск потоков. Закомментировав все это, теперь все работает.

Другие вопросы по тегам