web-dev-qa-db-ja.com

Windowsスレッド:_beginthread vs _beginthreadex vs CreateThread C ++

スレッドを開始するより良い方法は、__beginthread_、__beginthreadx_、またはCreateThreadですか?

__beginthread_、__beginthreadex_、およびCreateThreadの利点/欠点を判断しようとしています。これらの関数はすべて、新しく作成されたスレッドにスレッドハンドルを返します。エラーが発生するとCreateThreadが少しの追加情報を提供することを既に知っています(GetLastErrorを呼び出すことで確認できます)...これらの機能をいつ使用するかを検討する必要がありますか?

私はWindowsアプリケーションで作業しているので、クロスプラットフォームの互換性はすでに問題外です。

私はmsdnのドキュメントを調べましたが、たとえば、なぜCreateThreadの代わりに_beginthreadを使用するか、またはその逆を選択するのかを理解できません。

乾杯!

更新:OK、すべての情報に感謝します。WaitForSingleObject()を使用した場合は_beginthread()を呼び出すことはできませんが、_endthread()スレッドでそれは機能しませんか?そこの取り決めは何ですか?

129
Kiril

CreateThread()は、カーネルレベルで別の制御スレッドを作成するための未加工のWin32 API呼び出しです。

_beginthread()_beginthreadex()は、CreateThread()をバックグラウンドで呼び出すCランタイムライブラリの呼び出しです。 CreateThread()が返されると、_beginthread/ex()が追加のブックキーピングを処理し、新しいスレッドでCランタイムライブラリを使用可能かつ一貫性のあるものにします。

C++では、Cランタイムライブラリ(別名MSVCRT * .dll/.lib)にまったくリンクしない場合を除き、ほぼ確実に_beginthreadex()を使用する必要があります。

94
Drew Hall

_beginthread()_beginthreadex()にはいくつかの違いがあります。 _beginthreadex()CreateThread()のように動作するように作成されました(両方のパラメーターとその動作において)。

Drew Hall が述べているように、C/C++ランタイムを使用している場合、_beginthread()の代わりに_beginthreadex()/CreateThread()を使用する必要があります。ランタイムは、独自のスレッド初期化(スレッドローカルストレージのセットアップなど)を実行する機会があります。

実際には、これはCreateThread()がコードによって直接使用されることはほとんどないことを意味します。

_beginthread()/_beginthreadex()のMSDNドキュメントには、違いについてかなり詳細な情報があります。より重要なことの1つは、_beginthread()によって作成されたスレッドのスレッドハンドルが「_beginthreadによって生成されたスレッドがすぐに終了する場合、_beginthreadの呼び出し元に返されたハンドルは無効であるか、さらに悪いことに別のスレッドを指している」.

CRTソースの_beginthreadex()に対するコメントは次のとおりです。

Differences between _beginthread/_endthread and the "ex" versions:

1)  _beginthreadex takes the 3 extra parameters to CreateThread
  which are lacking in _beginthread():
    A) security descriptor for the new thread
    B) initial thread state (running/asleep)
    C) pointer to return ID of newly created thread

2)  The routine passed to _beginthread() must be __cdecl and has
  no return code, but the routine passed to _beginthreadex()
  must be __stdcall and returns a thread exit code.  _endthread
  likewise takes no parameter and calls ExitThread() with a
  parameter of zero, but _endthreadex() takes a parameter as
  thread exit code.

3)  _endthread implicitly closes the handle to the thread, but
  _endthreadex does not!

4)  _beginthread returns -1 for failure, _beginthreadex returns
  0 for failure (just like CreateThread).

更新2013年1月:

VS 2012のCRTには、_beginthreadex()で追加の初期化が行われます。プロセスが「パッケージアプリ」の場合(GetCurrentPackageId()から何か有用なものが返される場合)、ランタイムはMTAを初期化します新しく作成されたスレッド。

36
Michael Burr

これは、_beginthreadexの中核にあるコードです(crt\src\threadex.cを参照):

    /*
     * Create the new thread using the parameters supplied by the caller.
     */
    if ( (thdl = (uintptr_t)
          CreateThread( (LPSECURITY_ATTRIBUTES)security,
                        stacksize,
                        _threadstartex,
                        (LPVOID)ptd,
                        createflag,
                        (LPDWORD)thrdaddr))
         == (uintptr_t)0 )
    {
            err = GetLastError();
            goto error_return;
    }

残りの_beginthreadexは、CRTのスレッドごとのデータ構造を初期化します。

_beginthread*を使用する利点は、スレッドからのCRT呼び出しが正しく機能することです。

17
Constantin

_beginthreadまたは_beginthreadexを使用して、Cランタイムライブラリが独自のスレッドの初期化を行えるようにする必要があります。 C/C++プログラマーのみがこれを知る必要があります。これは、独自の開発環境を使用するルールを理解する必要があるためです。

_beginthreadを使用する場合、RTLが行うようにCloseHandleを呼び出す必要はありません。 _beginthreadを使用した場合、ハンドルを待つことができないのはこのためです。また、_beginthreadは、スレッド関数がすぐに(迅速に)終了すると混乱を引き起こします。これは、起動スレッドが起動したばかりのスレッドへの無効なスレッドハンドルを保持しているためです。

_beginthreadexハンドルは待機に使用できますが、CloseHandleの明示的な呼び出しも必要です。これは、waitを使用しても安全な理由の一部です。それを完全に確実にする他の問題は、常に中断されたスレッドを開始することです。成功、レコードハンドルなどを確認します。再開スレッド。これは、起動スレッドがハンドルを記録できるようになる前にスレッドが終了するのを防ぐために必要です。

ベストプラクティスは、_beginthreadexを使用し、サスペンドを開始し、ハンドルの記録後に再開し、ハンドルでの待機はOKで、CloseHandleを呼び出す必要があります。

12
jarcher7

CreateThread()はメモリリークが発生していました コードでCRT関数を使用する場合。 _beginthreadex()にはCreateThread()と同じパラメーターがあり、_beginthread()よりも汎用性があります。したがって、_beginthreadex()を使用することをお勧めします。

8
Jaywalker

関数シグネチャを見ると、CreateThread_beginthreadexとほとんど同じです。

_beginthread_beginthreadx vs CreateThread

HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);

uintptr_t _beginthread( 
   void( *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);

uintptr_t _beginthreadex( 
   void *security,
   unsigned stack_size,
   unsigned ( *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);

here の備考では、_beginthread__cdeclまたは__clrcall呼び出し規約を開始点として使用でき、_beginthreadex__stdcallまたは__clrcallを開始点として使用できます。

CreateThreadのメモリリークに関する人々のコメントは10年以上前のものであり、おそらく無視すべきだと思います。

興味深いことに、両方の_beginthread*関数は、実際には、マシンのC:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\srcで、内部でCreateThreadを呼び出します。

// From ~line 180 of beginthreadex.c
/*
 * Create the new thread using the parameters supplied by the caller.
 */
if ( (thdl = (uintptr_t)
      CreateThread( (LPSECURITY_ATTRIBUTES)security,
                    stacksize,
                    _threadstartex,
                    (LPVOID)ptd,
                    createflag,
                    (LPDWORD)thrdaddr))
         == (uintptr_t)0 )
{
        err = GetLastError();
        goto error_return;
}
5
bobobobo

更新された質問について:「WaitForSingleObject()を使用した場合、_beginthread()を呼び出せないが、_endthread()を呼び出した場合、スレッドは動作しませんか?」

一般に、スレッドハンドルをWaitForSingleObject()(またはオブジェクトハンドルで待機する他のAPI)に渡して、スレッドが完了するまでブロックできます。ただし、_beginthread()が呼び出されると、_endthread()によって作成されたスレッドハンドルは閉じられます(これは、明示的に実行するか、スレッドプロシージャが戻るときに実行時に暗黙的に実行されます)。

問題はWaitForSingleObject()のドキュメントに記載されています。

待機がまだ保留中にこのハンドルが閉じられた場合、関数の動作は未定義です。

5
Michael Burr

beginthreadexは、HANDLEおよび友人で使用するスレッドWaitForSingleObjectを提供します。 beginthreadはしません。完了したら、CloseHandle()を忘れないでください。本当の答えは、boost::threadを使用するか、まもなくC++ 09のスレッドクラスを使用することです。

3
Gert-Jan

に比べ _beginthread_beginthreadex あなたはできる:

  1. セキュリティ属性を指定します。
  2. 中断状態でスレッドを開始します。
  3. OpenThreadで使用できるスレッドIDを取得できます。
  4. 返されたスレッドハンドルは、呼び出しが成功した場合に有効であることが保証されています。そこで、CloseHandleでハンドルを閉じる必要があります。
  5. 返されるスレッドハンドルは、同期APIで使用できます。

_beginthreadexCreateThreadによく似ていますが、前者はCRT実装で、後者はWindows API呼び出しです。 CreateThread のドキュメントには、次の推奨事項が含まれています。

Cランタイムライブラリ(CRT)を呼び出す実行可能ファイル内のスレッドは、 _beginthreadex および _endthreadexCreateThreadおよび ExitThread ;これには、マルチスレッドバージョンのCRTを使用する必要があります。 CreateThreadを使用して作成されたスレッドがCRTを呼び出すと、CRTはメモリ不足の状態でプロセスを終了する場合があります。

2
Vishal

CreateThread()は、言語に依存しないWindows API呼び出しです。 OSオブジェクト-スレッドを作成し、このスレッドにハンドルを返します。すべてのWindowsアプリケーションは、この呼び出しを使用してスレッドを作成しています。すべての言語は、明らかな理由で直接API呼び出しを回避します。

_beginthreadex()CreateThread()のCラッパーで、C固有のものを考慮します。スレッド固有のストレージを割り当てることにより、マルチスレッド環境で元のシングルスレッドC f-nsを動作させることができます。

CRTを使用しない場合、CreateThread()の直接呼び出しを避けることはできません。 CRTを使用する場合は、_beginthreadex()を使用する必要があります。そうしないと、VC2005以前では一部のCRT文字列f-nsが正常に機能しない場合があります。

2
SKV

CreateThread() onceは、CRTが誤って初期化/クリーンアップされるため、no-noでした。しかし、これが今の歴史です。CRTを壊さずにCreateThread()を呼び出すことができます(VS2010とおそらくいくつかのバージョンを使用して)。

ここに公式のMS確認があります 。例外が1つあります。

実際、CreateThread()で作成されたスレッドで使用すべきでない唯一の関数はsignal()関数です。

しかし、一貫性の観点から、私は個人的に_beginthreadex()を使い続けることを好みます。

2
Serge Wautier

CreateThread()はストレートシステムコールです。これはKernel32.dllに実装されており、ほとんどの場合、アプリケーションは他の理由で既にリンクされています。最新のWindowsシステムでは常に利用可能です。

_beginthread()および_beginthreadex()は、Microsoft Cランタイムのラッパー関数(msvcrt.dll)です。 2つの呼び出しの違いは、ドキュメントに記載されています。したがって、Microsoft Cランタイムが使用可能な場合、またはアプリケーションが静的にリンクされている場合に使用できます。純粋なWindows APIでコーディングしている場合を除き(おそらく私が個人的によく行っているように)、そのライブラリに対してもリンクする可能性があります。

あなたの質問は首尾一貫したものであり、実際には反復的なものです。多くのAPIと同様に、Windows APIには対処する必要がある重複したあいまいな機能があります。最悪なことに、ドキュメントでは問題が明確になっていません。 _beginthread()ファミリの関数は、errnoの操作など、他の標準C機能との統合を改善するために作成されたと思われます。 _beginthread()は、Cランタイムとより良く統合します。

それにもかかわらず、_beginthread()または_beginthreadex()を使用する正当な理由がない限り、CreateThread()を使用する必要があります。これは、主に最終実行可能ファイルのライブラリ依存性が1つ少ないためです(MS CRTでは少し重要です)。また、呼び出しをラップするコードはありませんが、この効果は無視できます。言い換えれば、CreateThread()にこだわる主な理由は、最初に_beginthreadex()を使用する正当な理由がないからだと思います。機能は正確に、またはほぼ同じです。

_beginthread()を使用する正当な理由の1つは、_endthread()が呼び出された場合にC++オブジェクトが適切に巻き戻されたり破棄されたりするになることです。

1
alecov

本の中でJeffrey RichterのDebugging Windows Applicationを読むと、ほとんどすべての場合、CreateThreadを呼び出す代わりに_beginthreadexを呼び出す必要があると説明しています。 _beginthreadは、_beginthreadexの単純なラッパーです。

_beginthreadexは、CreateThread AP​​Iではできない特定のCRT(C RunTime)内部を初期化します。

CRT関数への_begingthreadex呼び出しの代わりにCreateThread AP​​Iを使用すると、予期しない問題が発生する可能性があります。

Richterからのこの古いMicrosoft Journalをご覧ください。

0
Ehsan Samani

他の回答では、Win32 API関数をラップするCランタイム関数を呼び出すことの意味を説明できません。これは、DLLローダーのロック動作を考慮する場合に重要です。

__beginthread{ex}_が他の回答で説明しているように特別なCランタイムスレッド/ファイバーメモリ管理を行うかどうかにかかわらず、それは(Cランタイムへの動的リンクを想定して)DLLに実装されますまだロードしていません。

DllMainから__beginthread*_を呼び出すのは安全ではありません。 Windowsの「AppInit_DLLs」機能を使用してロードされたDLLを記述して、これをテストしました。 _beginthreadex (...)の代わりにCreateThread (...)を呼び出すと、ローダーのロックが解除されるのを待っているDllMainエントリポイントデッドロックが発生するため、Windowsの重要な部分の多くが起動中に機能しなくなります特定の初期化タスクを実行するため。

ちなみに、これがkernel32.dllがCランタイムにもある多くの重複する文字列関数を持っている理由でもあります-同じような状況を避けるためにDllMainの関数を使用します。

0