3

クラスの 1 つのコンストラクターで、CreateThread最後の操作として Windows 関数を呼び出します。すぐに実行するスレッドが作成され、クラスの this ポインターを として渡しますlpParameter

スレッド プロシージャで、渡されたパラメーターをクラスのポインターにキャストし、名前を付けますpThis

を呼び出したときに渡したポインタと同じメモリ位置pThisを指していることがわかります。ただし、アクセスされるメンバー変数を見ると、それらはすべて間違った値を持っています。thisCreateThreadpThis->...

this->member_variablethis ポインターが属するクラスの usedの値はpThis->member_variable、スレッドのプロシージャーに記述したときに取得する値と同じであると予想していました。

CreateThread別のメンバー関数 (コンストラクターではない)を呼び出すと、すべてが正しく動作します。

したがって、質問: CreateThreadC++ クラスのコンストラクター内から Windows 関数を呼び出すことは禁止されていますか? はいの場合、何が問題ですか?

説明:

1) オブジェクトが常に存在することを確認できます。オブジェクトは、プログラム全体が終了したときにのみスコープから外れます。既に述べたようにCreateThread、他のメンバー関数からの呼び出しは機能します。

2) 「有線」の誤字を修正しました。「変」だったはずです。申し訳ありません。

いくつかのコード:

「欠陥のある」部分を維持しながら、物事を最小限に減らすコードスニペットを投稿しようとしています。

class CTimerW32 : public CTimer
{
    public:
        CTimerW32();
        ~CTimerW32();

    private:
        static void CALLBACK TimerCallback(LPVOID lpParam, BOOLEAN bReason);
        static DWORD WINAPI WaitCompletition(LPVOID lpParam);

    private:
        HANDLE m_hCompletitionEvent;
        HANDLE m_hCompletitionThread;
        bool m_bStartDeferred;
};

CTimer基本クラスは、さまざまなプラットフォームでのビルドを可能にする単なる抽象基本クラスであるため、安全に無視できます。

CTimerW32::CTimerW32()
{
    m_bStartDeferred= false;
    m_hCompletitionEvent= CreateEvent(NULL, FALSE, FALSE, NULL);
    m_hCompletitionThread= CreateThread(NULL, 0, WaitCompletition, this, 0, NULL);
}

m_hCompletitionEventここで、 への呼び出し後に有効であることがわかりますCreateEvent

DWORD WINAPI CTimerW32::WaitCompletition(LPVOID lpParam)
{
    CTimerW32* pThis;
    DWORD dwRet;

    pThis= (CTimerW32*)(lpParam);

    while (true) {
        // just wait for the completition event to be signaled
        dwRet= WaitForSingleObject(pThis->m_hCompletitionEvent, INFINITE);

        // ...
        if (pThis->m_bStartDeferred) {
            // ...
        }
    }

ここで、 への呼び出しで問題が発生していますWaitForSingleObject。既に述べたように、クラスCTimerW32(現在pThis) のオブジェクトの this ポインターは、スレッドの作成時に this ポインターと同じ値を保持しています。ただし、ハンドルはpThis->m_hCompletitionEventランダム データのようです。CreateEventコンストラクターでの呼び出し後に観察される値ではありません。

4

2 に答える 2

2

コンストラクターでスレッドを作成することは問題ではありません。また、コンストラクター内のコードを実行してスレッドを作成する前に、オブジェクトを初期化子リストで完全に初期化する必要があるため、初期化はおそらく問題ではありません。

監視しているオブジェクトが範囲外であり、新しいスレッドで監視する前にそのデストラクタが呼び出されている可能性があります。newを使用してオブジェクトを動的に作成してみて、これがまだ発生するかどうかを確認してください。オブジェクトがスコープから外れたときにオブジェクトが破棄されないため、そうではないはずです。

明らかに、このオブジェクトへのポインタをより高いスコープに保持して、最終的にも削除できるようにする必要があります:)

于 2012-07-25T16:36:18.960 に答える
0

Application Verifierの助けを借りて、この問題をデバッグするのがおそらく最善の方法です。プログラムの「基本」オプションをオンにすると、PageHeap が有効になり、メモリが解放されるとすぐにエラーになります。タイマー変数をスタック割り当てしている場合は運が悪くなりますが、破損に気付いたときに、タイマーを作成したスレッドがまだ関数内にあるかどうかをデバッガーで確認できるはずです。 CTimerW32 関数が宣言されました。

最後に、このユースケースでは、スレッドプール タイマー APIは、独自の専用スレッドを作成するよりも簡単に機能し、リソースの消費も少なくて済みます。

于 2012-08-20T02:36:02.127 に答える