Windowsスレッディング:_beginthreadと_beginthreadexとCreateThread C ++


133

スレッドを開始するためのより良い方法は何ですか_beginthread_beginthreadxまたはCreateThread

私はの長所/短所が何であるかを判断しようとしている_beginthread_beginthreadexCreateThread。これらのすべての関数は、新しく作成されたスレッドにスレッドハンドルを返します。エラーが発生すると、CreateThreadが少し追加の情報を提供することをすでに知っています(を呼び出すことで確認できますGetLastError)... mこれらの関数を使用していますか?

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

私はmsdnのドキュメントを読みましたが、たとえば、なぜCreateThreadではなく_beginthreadを使用するのか、またはその逆を行うのかを理解できません。

乾杯!

更新:わかりました。すべての情報に感謝します。WaitForSingleObject()を使用_beginthread()した場合は呼び出すことができない場所もいくつか読んだことがあります_endthread()が、スレッドで呼び出す場合は機能しませんか?そこでの契約は何ですか?


2
ここで何_beginthreadexの分析は、()C / C ++プログラマのためのない私はイーライBenderskyのウェブサイト上のリンクから見つけました。これは、CreateThread()を使用するかどうかに関するQ&Aからのものです。 microsoft.com/msj/0799/win32/win320799.aspx
Richard Chambers

回答:


96

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

_beginthread()_beginthreadex()CreateThread()、バックグラウンドで呼び出すCランタイムライブラリの呼び出しです。CreateThread()戻ったら_beginthread/ex()、Cランタイムライブラリを新しいスレッドで使用可能かつ一貫性のあるものにするために、追加の簿記を処理します。

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


39
これは、以前のように真実ではなくなっています。CRTは、signal()関数を除いて、CreateThread()によって作成されたスレッドで正しく機能します。CRTを使用するCreateThread()で作成されたスレッドごとに小さなメモリリーク(〜80バイト)がありますが、正しく機能します。詳細については、support.microsoft.com
default.aspx / kb / 104641

1
@ジョン:実際、そのバグはMSVC ++ 6.0
bobobobo

5
@bobobobo:いい質問ですね。私は、MSがもともと_beginルーチンを内部呼び出しにすることを意図してCreateThreadいて、誰もが呼び出すAPI関数であるはずだったと推測することができます。もう1つの潜在的な説明は、MSには、標準を無視して、名前の付け方について非常に悪い決定を下すという、長く輝かしい歴史があるということです。
John Dibling 2010

15
_begin関数は、アンダースコアで始まるため、 Microsoftはより密接標準に従うことを始めました。Cランタイムでは、アンダースコア付きの名前は実装用に予約されています(実装は、これらと同様に、エンドユーザーが使用できるように文書化できます)。beginthreadex()ユーザーが使用できる名前です。Cランタイムがそれを使用した場合、ユーザーが使用することを期待できる正当な権利を持っているエンドユーザーシンボルと競合する可能性があります。Win32 APIはCランタイムの一部ではなく、ユーザーの名前空間を使用することに注意してください。
Michael Burr

2
@Lothar:Win32 API呼び出しとCRT呼び出しに違いがあり、スレッドでCRTを呼び出す場合CreateThread_beginthread/ex、常にで作成する必要があります_beginthread/ex。そうしないと、メモリリークがなくなる可能性があります。しかしCreateThread、たとえばを呼び出すときに、浮動小数点環境が適切に初期化されないことは確かです。もっとあります「スレッドが使用して作成した場合のCreateThreadは、 CRTを呼び出し、CRTが低メモリ条件での処理を終了させることができます。」
IInspectable 2016

37

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

以下のようドリューホール言及あなたがC / C ++ランタイムを使用している場合、あなたは使用する必要があります_beginthread()/ _beginthreadex()の代わりに、CreateThread()ランタイムは、それが(など、スレッドローカルストレージを設定する)自身のスレッドの初期化です実行する機会を持っているように。

実際には、CreateThread()コードで直接使用することはほとんどありません。

_beginthread()/ のMSDNドキュメント_beginthreadex()には、違いに関するかなりの詳細があります-により作成されたスレッドのスレッドハンドルは_beginthread()、スレッドの終了時にCRTによって自動的に閉じられるため、「_ beginthreadによって生成されたスレッドが終了した場合すぐに、_beginthreadの呼び出し元に返されたハンドルが無効になるか、さらに悪いことに、別のスレッドを指す可能性があります。」

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

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を初期化します。


3
そこているのCreateThread()が保証され、適切な時間は、正直なところ、あなたは実際にそれを行うにはあなたの方法で外出しなければなりません。私たちは、移植可能なものの完全な欠如について話し、排他的にWIN32 API DLLまたはアプリを作成しています。Cランタイム呼び出しは含まれません。WIN32メモリ管理関数を使用するには、カスタムアロケーターを提供する必要があるという点で、STLの使用でさえ制限されます。Developer Studioでこれを行うための設定は、それ自体が仕事ですが、可能な限り最小のフットプリントを持つ唯一のWIN32 libの場合は、実行できます。しかし、そうです、非常に少数の人を除いて、ほとんどすべての人にとってそれは血まみれではありません。
WhozCraig

1
@WhozCraig:CRTを省略すると、さらに厳しい制限があります。最も顕著なものは、64ビット整数のサポートなし、浮動小数点サポートなし、そして最も劇的に-例外処理なしです。これは本当に例外処理がまったくないことを意味します。SEH例外すらありません。これは補うのが特に難しくCreateThread、正しいことであると呼ぶ可能性は非常に薄いです。
IInspectable 2014


@Mehrdad:特に言及する価値のある変更点は何ですか?
IInspectable 2017

DisableThreadLibraryCallsはCreateThreadで作成されたスレッドには影響を与えませんが、_beginthreadまたは_beginthreadexで作成されたスレッドを無効にします。
SPlatten

23

一般に、正しいことは、呼び出し_beginthread()/_endthread()(またはex()バリアント)することです。しかし、あなたがた.dllとしてCRTを使用する場合、CRTのとして、CRTの状態が適切に初期化され、破壊されるDllMainと呼び出されますDLL_THREAD_ATTACHDLL_THREAD_DETACH呼び出すときCreateThread()ExitThread()、それぞれ、または返します。

DllMainCRT のコードは、VSのインストールディレクトリのVC \ crt \ src \ crtlib.cにあります。


素晴らしい出発点。少しデバッグすれば、静的にリンクされたCRTの場合でも__CRTDLL_INITが呼び出されていることがわかります。コールスタックinitは_LdrpCallInitRoutine @ 16()から呼び出されますが、正確にはどのメカニズムかわかりません。つまり、最近のCRTでは、シグナル処理を除いて、すべての初期化/非初期化が正しく行われます。これは、beginthreadからは呼び出されますが、CreateThreadからは呼び出されない_threadstartexヘルパー関数で行われます。おそらくあなたはこれを答えに加えることができ、私は賞金を授与しますか?
スマ

これが最も役立つと思われるため、賞金が授与されます。それでも、答えはおそらく更新する価値があるかもしれません。できない場合は、数日以内に再訪問する場合があります。
Suma

1
@MSN:静的CRTに再度リンクていて、DLL_THREAD_DETACHの呼び出しを無効にするDisableThreadLibraryCallsを呼び出している場合は、DLLでCreateThreadが依然として悪いことに注意してください。次に、メモリリークが発生します。これは私のKB記事のここに記載されています:support.microsoft.com/kb/555563/en-us
Jochen Kalmbach

17

これは_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呼び出しが正しく機能することです。


12

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

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

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

ベストプラクティスは、を使用し_beginthreadex、一時停止を開始し、ハンドルの記録後に再開し、ハンドルを待機しても問題CloseHandleないので、呼び出す必要があります。


8

CreateThread()コードでCRT関数を使用すると、メモリリークが発生していました。_beginthreadex()と同じパラメータがCreateThread()あり、に比べて用途が広いです_beginthread()。ですから、を使用することをお勧めします_beginthreadex()


2
1999年の記事、それ以降修正された可能性がある
ボボボボ2010

1
2005年のこの記事は、まだ問題があることを確認しています。
Jaywalker

2
はい、MSVC ++ 6.0 Service Pack 5以前にのみ適用されます。(「適用対象」の展開可能なドロップダウンを参照してください)。VC7以上を使用している場合、これは今日の問題ではありません。
ボボボボ

1
静的CRTを再度リンクすると、これはまだ問題です!また、静的にリンクされているDLLでDisableThreadLibraryCallsを呼び出す場合にも問題があります。私のKB記事を参照してください:support.microsoft.com/kb/555563/en-us
Jochen Kalmbach

2
あなたは情報を誤って伝えました:決してメモリをリークCreateThreadしませ。正しく初期化されていないスレッドから呼び出された場合は、むしろCRTです。
IInspectable 2014

6

更新された質問について:「WaitForSingleObject()使用_beginthread()した場合に呼び出すことができない場所もいくつか読んだことがあります_endthread()が、スレッドで呼び出す場合は機能しませんか?」

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

この問題は、次のドキュメントで説明されていますWaitForSingleObject()

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


5

関数のシグネチャを見ると、CreateThreadとほぼ同じです_beginthreadex

_beginthread_beginthreadx vsCreateThread

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 
);

上の発言ここでは言う_beginthreadのいずれかを使用することができます__cdeclまたは__clrcall開始点として呼び出し規約、および_beginthreadexいずれかを使用することができます__stdcallまたは__clrcall開始ポイントのために。

私がメモリリークに関して行ったコメントCreateThreadは10年以上前のものであり、おそらく無視する必要があります。

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

// 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;
}

2
あなたがのCreateThreadを呼び出し、そのスレッド上で呼び出します(旧間違いない十年、およびCRTに混ぜてはならない理由についてのコメント、絶対に無視すべきではありません)「スレッドはのCreateThreadは、CRTを呼び出して使用して作成した場合、CRTはプロセスを終了させることができますメモリ不足の状態。」
IInspectable 2016

3

beginthreadexと友達にHANDLE使用するためのスレッドを提供しますWaitForSingleObjectbeginthreadしません。CloseHandle()終わったら忘れずに。本当の答えは、boost::threadC ++ 09のスレッドクラスを使用することです。


msdnの説明では、「成功すると、これらの各関数は新しく作成されたスレッドへのハンドルを返します。」_beginthread()および_beginthreadex()を参照...
Kiril

@Kiril:しかし、ドキュメントには、_beginthreadがハンドルを閉じると記載されています。つまり、スレッドがすぐに終了すると、ハンドルを使用できなくなります...
Roger Lipscombe

2

と比較して_beginthread_beginthreadex次のことができます。

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

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

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


API仕様によると、箇条書き3〜5はに固有のものではありません_beginthreadexuintptr_t両方の関数からの戻り値をにキャストできHANDLEます。
Andon M. Coleman

はい、あなたは理論的に正しいです。実際には、違いは_beginthread終了時にハンドルを閉じることです。そのため、同期APIでハンドルを確実に使用したり、別の方法でハンドルを同期および複製したりしない限り、スレッドIDを取得することはできません。しかし、_beginthreadexあなたのためにそれをしているのです。
Vishal

2

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

これは公式のMS確認です。それは一つの例外を述べています:

実際には、で作成したスレッドで使用すべきではない唯一の機能がCreateThread()あるsignal()機能。

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


これは正しいと思いますが、MSドキュメントにリンクするか、CRT _beginthreadex / _endthreadexソースを分析することによって、信頼できる証拠を提供できますか?
スマ

@スマ、私はあなたがあなたのコメントをタイプしている間にMSリンクを追加したと思います;-)
セルジュ・ワティエ

リンクしているドキュメントは、「ただし、呼び出されるCRT関数によっては、スレッドが終了したときに小さなメモリリークが発生する可能性があります。」という確認はできないようです。つまり、スレッドが頻繁に作成され、それらの関数がスレッドで使用されている場合、それはもはや大きくて一般的な禁止事項ではなく、禁止事項ではありません。ただし、この文書は2005年のものであるため、最近の状況に対処することはできません。
スマ

1
うーん...それはユースケースに依存するかもしれませんが、メモリリークを残す関数は、どのサイズであっても、私はノーノーと考えます...特にリーキングしない代替がある場合!
アルク

「CRTを壊すことなくCreateThread()を呼び出せるようになりました。」-残念ながら、これは正しくありません。CreateThreadから:"Cランタイムライブラリ(CRT)を呼び出す実行可能ファイル内のスレッドは、スレッド管理に_beginthreadex関数と_endthreadex関数を使用する必要があります[...] CreateThreadを使用して作成されたスレッドがCRTを呼び出す場合、CRTはメモリ不足の状態で処理します。」
IInspectable 2016

2

CreateThread()言語に依存しないWindows API呼び出しです。OSオブジェクト-スレッドを作成し、このスレッドにHANDLEを返します。すべてのWindowsアプリケーションは、この呼び出しを使用してスレッドを作成しています。すべての言語は、明らかな理由で直接API呼び出しを回避します。1。コードをOS固有にしたくない2. APIのように呼び出す前に、ハウスキーピングを行う必要があります。パラメーターと結果の変換、一時ストレージの割り当てなど。

_beginthreadex()CreateThread()C固有のアカウントを囲むCラッパーです。スレッド固有のストレージを割り当てることにより、マルチスレッド環境で元のシングルスレッドC f-nsを機能させることができます。

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


1

CreateThread()ストレートシステムコールです。上でそれは実現していますKernel32.dllこれは、おそらく、あなたのアプリケーションがすでに他の理由のためにリンクされます。最新のWindowsシステムでは常に利用可能です。

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

あなたの質問は首尾一貫しており、実際には繰り返しの質問です。多くのAPIと同じように、処理する必要があるWindows APIには重複した曖昧な機能があります。最悪の場合、ドキュメントでは問題が明確にされていません。_beginthread()関数ファミリーは、の操作など、他の標準C機能との統合を改善するために作成されたと思いますerrno_beginthread()したがって、Cランタイムとよりよく統合されます。

それにもかかわらず、_beginthread()またはを使用する十分な理由がない限り_beginthreadex()、を使用する必要がありますCreateThread()。主な理由は、最終的な実行可能ファイルでライブラリの依存関係が1つ少なくなる可能性があるためです(MS CRTの場合、これは少し重要です)。また、この影響は無視できますが、呼び出しの周りにラッピングコードはありません。つまり、こだわりの主な理由CreateThread()は、そもそも使う理由が無いからだと思います_beginthreadex()。機能は正確に、またはほぼ同じです。

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


何のあいまいな関数呼び出しはありませんすべてではCreateThreadスレッドを作成するためのWindows API呼び出しです。CRTを使用している場合(CまたはC ++でプログラミングしているため)、CRTの_beginthread[ex]呼び出し(CreateThread必要なCRT初期化の実行に加えて呼び出す)を使用してスレッドを作成する必要があります。_beginthreadとex-variant の最も重要な違い:前者はネイティブスレッドハンドルの所有権を保持し、後者は所有者を呼び出し側に渡します。
IInspectable 2016

Nitpick:CランタイムDLLでmsvcrt.dllはありませんblogs.msdn.microsoft.com/oldnewthing/20140411-00/?p=1273
Govind Parmarを

0

その他の回答では、Win32 API関数をラップするCランタイム関数を呼び出すことの影響については説明していません。DLLローダーのロック動作を検討する場合、これは重要です。

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

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

ちなみに、これは、kernel32.dllに Cランタイムも実行する多くの重複する文字列関数がある理由でもあります。これらを使用しDllMainて、同じ種類の状況を回避します。


0

その本の中でJeffrey RichterからのDebugging Windows Applicationの本を読んだ場合、ほとんどすべての場合において、を呼び出す_beginthreadex代わりに呼び出す必要があると彼は説明していますCreateThread_beginthreadは単純化されたラッパー_beginthreadexです。

_beginthreadexCreateThreadAPIが実行しない特定のCRT(C RunTime)内部を初期化します。

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

この古いMicrosoft Journal From Richterをチェックしてください。


-2

両者に違いはありません。

メモリリークなどに関するコメントはすべて、非常に古い<VS2005バージョンに基づいています。私は何年か前にいくつかのストレステストを行ったことがあり、この神話を覆すことができます。Microsoftでさえ、それらの例にスタイルを混在させ、_beginthreadを使用することはほとんどありません。



「CRTのマルチスレッドバージョンを使用する必要があります」というサブセンテンスに基づいて、マルチスレッドのcrtバージョンがもう何年も存在しないため、これはドキュメントのガベージであると想定しています。
Lothar

「そこには、マルチスレッドのCRTバージョンはもうありません」 - MSDNの主張ということ「[T]彼は、CRTが使用できなくなったシングルスレッドません。」両方が正しいわけではありません。ここでもMSDNを使用します。
IInspectable 2016

これはタイプミスでした。もちろん、シングルスレッドがなくなり、マルチスレッドが標準になったことを意味します。なくなったのは、スレッドを使用するかどうかの違いです。
Lothar

これは本当におかしくなっています。あなたは今、間違いなく正しい("CRTのマルチスレッドバージョンの使用が必要です")ステートメントを使用しており、このステートメントとドキュメントの残りの部分の両方が間違っている可能性が高いと主張していますか?それは確かに正しく聞こえません。
IInspectable 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.