Windows luồng: _beginthread vs _beginthreadex vs CreateThread C ++


133

Cách tốt hơn để bắt đầu một chủ đề _beginthread, _beginthreadxhoặc CreateThread?

Tôi đang cố gắng xác định những lợi thế / bất lợi của _beginthread, _beginthreadexCreateThread. Tất cả các hàm này trả về một xử lý luồng cho một luồng mới được tạo, tôi đã biết rằng CreatThread cung cấp thêm một chút thông tin khi xảy ra lỗi (có thể được kiểm tra bằng cách gọi GetLastError) ... nhưng một số điều tôi nên xem xét khi tôi ' m sử dụng các chức năng này?

Tôi đang làm việc với một ứng dụng Windows, vì vậy khả năng tương thích đa nền tảng đã không còn nữa.

Tôi đã xem qua tài liệu msDN và tôi không thể hiểu được, ví dụ, tại sao mọi người sẽ quyết định sử dụng _beginthread thay vì CreatThread hoặc ngược lại.

Chúc mừng!

Cập nhật: OK, cảm ơn vì tất cả thông tin, tôi cũng đã đọc ở một vài nơi mà tôi không thể gọi WaitForSingleObject()nếu tôi đã sử dụng _beginthread(), nhưng nếu tôi gọi _endthread()trong chuỗi thì không nên làm việc đó? Thỏa thuận ở đó là gì?


2
Dưới đây là phân tích về những gì _beginthreadex () làm cho các lập trình viên C / C ++ mà tôi tìm thấy từ một liên kết trên trang web của Eli Bendersky. Đây là từ một câu hỏi và trả lời về việc có nên sử dụng CreatThread () hay không. microsoft.com/msj/0799/win32/win320799.aspx
Richard Chambers

Câu trả lời:


96

CreateThread() là một lệnh gọi API Win32 thô để tạo một luồng điều khiển khác ở cấp hạt nhân.

_beginthread()& _beginthreadex()là các cuộc gọi thư viện thời gian chạy C gọi CreateThread()phía sau hậu trường. Một khi CreateThread()đã trở lại, _beginthread/ex()chăm sóc sổ sách bổ sung để làm cho thư viện thời gian chạy C có thể sử dụng và nhất quán trong luồng mới.

Trong C ++, bạn gần như chắc chắn nên sử dụng _beginthreadex()trừ khi bạn sẽ không liên kết với thư viện thời gian chạy C (hay còn gọi là MSVCRT *. / .Lib).


39
Điều này không còn đúng như trước đây nữa. CRT sẽ hoạt động chính xác trong một luồng được tạo bởi CreatThread () ngoại trừ hàm signal signal (). Sẽ có một rò rỉ bộ nhớ nhỏ (~ 80 byte) cho mỗi luồng được tạo bằng CreateThread () sử dụng CRT, nhưng nó sẽ hoạt động chính xác. Xem để biết thêm thông tin: support.microsoft.com/default.aspx/kb/104641
John Dibling

1
@ John: Trên thực tế, lỗi đó chỉ áp dụng tối đa cho MSVC ++ 6.0
bobobobo

5
@bobobobo: Câu hỏi hay. Tôi chỉ có thể suy đoán rằng ban đầu MS dự định các _beginthói quen là các cuộc gọi nội bộ và CreateThreadđược cho là chức năng API mà mọi người sẽ gọi. Một lời giải thích tiềm năng khác là MS có một lịch sử lâu dài và vinh quang khi bỏ qua tiêu chuẩn và đưa ra những quyết định rất tồi tệ về việc đặt tên cho mọi thứ.
John Dibling

15
Các _beginchức năng bắt đầu bằng một dấu gạch dưới Microsoft bắt đầu tuân theo tiêu chuẩn chặt chẽ hơn. Trong thời gian chạy C, các tên có dấu gạch dưới được dành riêng cho việc triển khai (và việc triển khai có thể ghi lại chúng cho người dùng cuối, như với những cái này). beginthreadex()là tên được phép cho người dùng sử dụng. Nếu thời gian chạy C đã sử dụng nó, thì nó có thể xung đột với biểu tượng người dùng cuối mà người dùng có quyền hợp pháp để có thể mong đợi sử dụng. Lưu ý rằng API Win32 không phải là một phần của thời gian chạy C và chúng sử dụng không gian tên của người dùng.
Michael Burr

2
@Lothar: Có sự khác biệt giữa các cuộc gọi Win32 API CreateThreadvà các cuộc gọi CRT _beginthread/ex, và khi gọi CRT về một chủ đề, nó luôn luôn nên được tạo ra với _beginthread/ex. Có thể không còn rò rỉ bộ nhớ, nếu bạn không. Nhưng bạn chắc chắn sẽ không nhận được môi trường dấu phẩy động của mình được khởi tạo đúng cách khi gọi CreateThread. Còn nữa : "Nếu một luồng được tạo bằng CreatThread gọi CRT, CRT có thể chấm dứt quá trình trong điều kiện bộ nhớ thấp."
IInspectable 8/2/2016

37

Có một số khác biệt giữa _beginthread()_beginthreadex(). _beginthreadex()đã được thực hiện để hành động giống như CreateThread()(trong cả hai tham số và cách nó hoạt động).

Như Drew Hall đề cập, nếu bạn đang sử dụng thời gian chạy C / C ++, bạn phải sử dụng _beginthread()/ _beginthreadex()thay vì CreateThread()để thời gian chạy có cơ hội thực hiện khởi tạo luồng của chính nó (thiết lập lưu trữ cục bộ của luồng, v.v.).

Trong thực tế, điều này có nghĩa là CreateThread()mã của bạn sẽ không bao giờ được sử dụng trực tiếp.

Các tài liệu MSDN cho _beginthread() / _beginthreadex()có khá nhiều chi tiết về sự khác biệt - một trong những điều quan trọng hơn là vì xử lý luồng cho luồng được tạo bởi _beginthread()CRT tự động đóng khi luồng thoát, "nếu luồng được tạo bởi _beginthread thoát một cách nhanh chóng, tay cầm được trả về cho người gọi của _beginthread có thể không hợp lệ hoặc tệ hơn là trỏ đến một luồng khác ".

Dưới đây là những gì các ý kiến ​​cho _beginthreadex()trong nguồn CRT phải nói:

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

Cập nhật tháng 1 năm 2013:

CRT cho VS 2012 có thêm một chút khởi tạo được thực hiện trong _beginthreadex(): nếu quy trình là "ứng dụng đóng gói" (nếu có thứ gì đó hữu ích được trả về GetCurrentPackageId()), bộ thực thi sẽ khởi tạo MTA trên luồng mới được tạo.


3
những thời điểm thích hợp khi CreatThread () được bảo hành, nhưng thành thật mà nói bạn thực sự phải đi ra ngoài để làm điều đó. Chúng ta đang nói về việc thiếu hoàn toàn mọi thứ di động và đang viết một ứng dụng hoặc ứng dụng API WIN32 độc quyền. Bao gồm không có cuộc gọi C-runtime. Ngay cả việc sử dụng STL cũng bị hạn chế ở chỗ bạn phải cung cấp các bộ cấp phát tùy chỉnh để sử dụng các chức năng quản lý bộ nhớ WIN32. Việc thiết lập để thực hiện điều này với Developer Studio là một công việc riêng, nhưng đối với một lib duy nhất từng có WIN32 với dấu chân nhỏ nhất có thể, nó có thể được thực hiện. Nhưng vâng, nó không có khả năng đẫm máu với hầu hết tất cả trừ một số rất ít người được chọn.
WhozCraig

1
@WhozCraig: Có nhiều hạn chế nghiêm trọng hơn khi bỏ qua CRT. Những cái nổi bật nhất là: Không hỗ trợ số nguyên 64 bit, không hỗ trợ dấu phẩy động và - quyết liệt nhất - không xử lý ngoại lệ. Điều này thực sự có nghĩa là không xử lý ngoại lệ - cả . Thậm chí không có ngoại lệ SEH. Điều này đặc biệt khó khăn để bù đắp, và cơ hội để gọi CreateThreadlà Điều đúng là giảm dần.
IInspectable


@Mehrdad: Những thay đổi cụ thể nào bạn thấy đáng nói đến?
IInspectable

Tôi đã phát hiện ra rằng DisableThreadL LibraryCalls không có tác dụng đối với các luồng được tạo bằng CreateThread, nhưng không vô hiệu hóa các luồng được tạo bằng _beginthread hoặc _beginthreadex.
SPlatten

23

Nói chung, điều cần làm là gọi _beginthread()/_endthread()(hoặc các ex()biến thể). Tuy nhiên, nếu bạn sử dụng CRT dưới dạng dll, trạng thái CRT sẽ được khởi tạo và hủy đúng cách vì CRT DllMainsẽ được gọi cùng DLL_THREAD_ATTACHDLL_THREAD_DETACHkhi gọi CreateThread()ExitThread()trả lại, tương ứng.

Các DllMainmã cho CRT có thể được tìm thấy trong thư mục cài đặt cho VS dưới VC \ crt \ src \ crtlib.c.


Điểm khởi đầu tuyệt vời. Với một chút gỡ lỗi, người ta có thể hiển thị __CRTDLL_INIT được gọi ngay cả đối với CRT được liên kết tĩnh. Callstack init được gọi từ _LdrpCallInitRoutine @ 16 (), tôi không chắc chắn chính xác bởi cơ chế nào. Điều này có nghĩa là với CRT gần đây, tất cả quá trình khởi tạo / khử khử được thực hiện chính xác ngoại trừ xử lý tín hiệu, vẫn được thực hiện trong hàm trợ giúp _threadstartex được gọi từ Beginthread, nhưng không phải từ CreatThread. Có lẽ bạn có thể thêm điều này vào câu trả lời và tôi sẽ trao giải thưởng?
Suma

Bounty trao tặng, vì điều này có vẻ hữu ích nhất. Tuy nhiên, câu trả lời có lẽ đáng để cập nhật. Nếu bạn không thể làm điều đó, tôi có thể xem lại trong vòng một vài ngày.
Suma

1
@MSN: Xin lưu ý rằng CreatThread vẫn còn tệ trong một DLL, nếu bạn đang liên kết lại CRT tĩnh đã gọi DisableThreadL LibraryCalls sẽ vô hiệu hóa các cuộc gọi cho DLL_THREAD_DETACH. Sau đó, bạn sẽ nhận được rò rỉ bộ nhớ. Điều này được ghi lại ở đây trong bài viết KB của tôi: support.microsoft.com/kb/555563/en-us
Jochen Kalmbach

17

Đây là mã ở cốt lõi của _beginthreadex(xem 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;
    }

Phần còn lại của _beginthreadexkhởi tạo cấu trúc dữ liệu trên mỗi luồng cho CRT.

Ưu điểm của việc sử dụng _beginthread*là các cuộc gọi CRT của bạn từ luồng sẽ hoạt động chính xác.


12

Bạn nên sử dụng _beginthreadhoặc_beginthreadex để cho phép thư viện thời gian chạy C thực hiện việc khởi tạo luồng của chính nó. Chỉ các lập trình viên C / C ++ mới cần biết điều này vì bây giờ họ nên sử dụng các quy tắc sử dụng môi trường phát triển của riêng họ.

Nếu bạn sử dụng, _beginthreadbạn không cần phải gọi CloseHandlevì RTL sẽ làm cho bạn. Đây là lý do tại sao bạn không thể chờ đợi trên tay cầm nếu bạn đã sử dụng _beginthread. Cũng thế_beginthread dẫn đến sự nhầm lẫn nếu chức năng luồng thoát ngay lập tức (một cách nhanh chóng) khi luồng khởi chạy của tôi bị giữ lại xử lý luồng không hợp lệ đối với luồng mà nó vừa khởi chạy.

_beginthreadextay cầm có thể được sử dụng để chờ nhưng cũng yêu cầu một cuộc gọi rõ ràng đến CloseHandle. Đây là một phần của những gì làm cho chúng an toàn để sử dụng với sự chờ đợi. Có một vấn đề khác để làm cho nó hoàn toàn dễ dàng là luôn luôn bắt đầu chuỗi bị treo. Kiểm tra thành công, xử lý hồ sơ, vv Chủ đề tiếp tục. Điều này là cần thiết để ngăn chặn một luồng kết thúc trước khi luồng khởi chạy có thể ghi lại xử lý của nó.

Thực hành tốt nhất là sử dụng _beginthreadex, bắt đầu treo sau đó tiếp tục sau khi xử lý ghi âm, chờ xử lý là OK, CloseHandlephải được gọi.


8

CreateThread()đã từng bị rò rỉ bộ nhớ khi bạn sử dụng bất kỳ hàm CRT nào trong mã của mình. _beginthreadex()có các thông số tương tự CreateThread()và nó linh hoạt hơn _beginthread(). Vì vậy tôi khuyên bạn nên sử dụng _beginthreadex().


2
Bài viết năm 1999, có thể đã được sửa chữa
bobobobo

1
Bài viết này từ năm 2005 vẫn xác nhận rằng có một vấn đề.
Jaywalker

2
Vâng, nó chỉ áp dụng cho MSVC ++ 6.0 Gói dịch vụ 5 trở về trước. (xem phần "Áp dụng cho" thả xuống có thể mở rộng). Đây không phải là vấn đề ngày hôm nay nếu bạn đang sử dụng VC7 trở lên.
bobobobo

1
Đây vẫn là một vấn đề, nếu bạn liên kết lại CRT tĩnh! Ngoài ra, đây vẫn là một vấn đề nếu bạn gọi DisableThreadL LibraryCalls trong một DLL được liên kết tĩnh; xem bài viết KB của tôi: support.microsoft.com/kb/555563/en-us
Jochen Kalmbach

2
Bạn đã bóp méo thông tin: CreateThreadkhông bao giờ bị rò rỉ bộ nhớ. Nó đúng hơn là CRT, khi được gọi từ một luồng chưa được khởi tạo đúng.
IInspectable

6

Về câu hỏi cập nhật của bạn: "Tôi cũng đã đọc ở một vài nơi mà tôi không thể gọi WaitForSingleObject()nếu tôi đã sử dụng _beginthread(), nhưng nếu tôi gọi _endthread()trong chuỗi thì không nên làm việc đó?"

Nói chung, bạn có thể chuyển một xử lý luồng cho WaitForSingleObject()(hoặc các API khác đang chờ xử lý đối tượng) để chặn cho đến khi luồng hoàn thành. Nhưng xử lý luồng được tạo bởi _beginthread()được đóng khi _endthread()được gọi (có thể được thực hiện rõ ràng hoặc được thực hiện hoàn toàn theo thời gian chạy khi thủ tục luồng trả về).

Vấn đề được gọi ra trong tài liệu cho WaitForSingleObject():

Nếu xử lý này được đóng trong khi chờ vẫn đang chờ xử lý, hành vi của chức năng không được xác định.


5

Nhìn vào chữ ký chức năng, CreateThreadgần như giống hệt _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 
);

Các nhận xét ở đây cho biết _beginthreadcó thể sử dụng __cdeclhoặc __clrcallgọi quy ước làm điểm bắt đầu và _beginthreadexcó thể sử dụng một trong hai __stdcallhoặc__clrcall cho điểm bắt đầu.

Tôi nghĩ rằng bất kỳ ý kiến ​​mọi người thực hiện về rò rỉ bộ nhớ trong CreateThread hơn một thập kỷ và có lẽ nên được bỏ qua.

Thật thú vị, cả hai _beginthread*chức năng thực sự gọi CreateThreaddưới mui xe, trong C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\srcmáy của tôi.

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


3

beginthreadexcung cấp cho bạn một chủ đề HANDLEđể sử dụng trong WaitForSingleObjectvà bạn bè. beginthreadkhông. Đừng quên CloseHandle()khi bạn hoàn thành. Câu trả lời thực sự sẽ là sử dụng boost::threadhoặc sớm lớp chủ đề của C ++ 09.


Mô tả msdn nói rằng "Nếu thành công, mỗi hàm này trả về một điều khiển cho luồng mới được tạo;" đề cập đến _beginthread () và _beginthreadex () ...
Kiril

@Kiril: nhưng sau đó tài liệu tiếp tục nói rằng _beginthread đóng tay cầm cho bạn, nghĩa là bạn không thể sử dụng nó nếu luồng thoát ra nhanh chóng ...
Roger Lipscombe

2

So với _beginthread, _beginthreadexbạn có thể:

  1. Chỉ định các thuộc tính bảo mật.
  2. Bắt đầu một chủ đề trong trạng thái lơ lửng.
  3. Bạn có thể lấy id chủ đề có thể được sử dụng với OpenThread.
  4. Xử lý luồng được trả về được đảm bảo là hợp lệ nếu cuộc gọi thành công. Có cho bạn cần phải đóng tay cầm với CloseHandle.
  5. Xử lý luồng được trả về có thể được sử dụng với API đồng bộ hóa.

Gần _beginthreadexgiống như vậy CreateThread, nhưng trước đây là một triển khai CRT và sau đó là một cuộc gọi API Windows. Tài liệu cho CreatThread chứa khuyến nghị sau:

Một luồng trong một tệp thực thi gọi thư viện thời gian chạy C (CRT) nên sử dụng các hàm _beginthreadex_endthreadexđể quản lý luồng hơn là CreateThreadExitThread; điều này đòi hỏi phải sử dụng phiên bản đa luồng của CRT. Nếu một luồng được tạo bằng cách CreateThreadgọi CRT, CRT có thể chấm dứt quá trình trong điều kiện bộ nhớ thấp.


Theo đặc tả API, các gạch đầu dòng 3-5 không phải là duy nhất _beginthreadex. Bạn có thể chuyển uintptr_ttrả lại từ cả hai chức năng đến HANDLE.
Andon M. Coleman

Vâng, bạn đúng trong lý thuyết. Trong thực tế, sự khác biệt là _beginthreadđóng tay cầm khi thoát. Vì vậy, bạn không thể tin cậy sử dụng tay cầm với API đồng bộ hóa hoặc để lấy id luồng cho đến khi và trừ khi bạn sử dụng một cách khác để đồng bộ hóa và sao chép tay cầm. Nhưng sau đó là _beginthreadexlàm điều đó cho bạn.
Vishal

2

CreateThread()một lần là không vì CRT sẽ khởi tạo / dọn dẹp không chính xác. Nhưng đây là lịch sử: Bây giờ người ta có thể (sử dụng VS2010 và có thể quay lại vài phiên bản) CreateThread()mà không phá vỡ CRT.

Đây là xác nhận chính thức của MS . Nó nêu một ngoại lệ:

Trên thực tế, chức năng duy nhất không nên được sử dụng trong một luồng được tạo ra CreateThread()signal()chức năng.

Tuy nhiên, từ quan điểm nhất quán, cá nhân tôi thích tiếp tục sử dụng _beginthreadex().


Trong khi tôi cho rằng điều này là đúng, bạn có thể cung cấp một số bằng chứng xác thực - bằng cách liên kết với Tài liệu MS hoặc bằng cách phân tích các nguồn CRT _beginthreadex / _endthreadex?
Suma

@Suma, tôi đoán tôi đã thêm liên kết MS trong khi bạn đang gõ bình luận của bạn ;-)
Serge Wautier

Tài liệu bạn đang liên kết dường như không xác nhận: "Tuy nhiên, tùy thuộc vào chức năng CRT nào được gọi, có thể có rò rỉ bộ nhớ nhỏ khi các luồng bị chấm dứt." Điều này có nghĩa là nó không còn là không lớn và nói chung, nhưng vẫn là không nếu bạn đang tạo chủ đề thường xuyên và sử dụng các chức năng đó trong chúng. Tuy nhiên, tài liệu là từ năm 2005 và do đó không thể giải quyết tình trạng gần đây của vấn đề.
Suma

1
Hmm ... mặc dù nó có thể phụ thuộc vào trường hợp sử dụng, một chức năng để lại rò rỉ bộ nhớ, bất kể kích thước nào, tôi sẽ xem xét không-không ... - đặc biệt nếu có một giải pháp thay thế không rò rỉ!
kiềm

"Giờ đây, người ta có thể gọi CreatThread () mà không phá vỡ CRT." - Thật không may, điều này là không đúng sự thật, và chưa bao giờ được. Từ CreateThread : "Một luồng trong tệp thực thi gọi thư viện thời gian chạy C (CRT) nên sử dụng các hàm _beginthreadex và _endthreadex để quản lý luồng [...] Nếu một luồng được tạo bằng CreatThread gọi CRT, CRT có thể chấm dứt xử lý trong điều kiện bộ nhớ thấp. "
IInspectable

2

CreateThread()là cuộc gọi API Windows là ngôn ngữ trung lập. Nó chỉ tạo đối tượng OS - luồng và trả về HandLE cho luồng này. Tất cả các ứng dụng windows đang sử dụng cuộc gọi này để tạo chủ đề. Tất cả các ngôn ngữ đều tránh lệnh gọi API trực tiếp vì lý do rõ ràng: 1. Bạn không muốn mã của mình là hệ điều hành cụ thể 2. Bạn cần thực hiện một số thao tác giữ nhà trước khi gọi API giống như: chuyển đổi tham số và kết quả, phân bổ lưu trữ tạm thời, v.v.

_beginthreadex()là C bao bọc xung quanh CreateThread()tài khoản đó cho C cụ thể. Nó cho phép C f-ns luồng đơn gốc hoạt động trong môi trường đa luồng bằng cách phân bổ lưu trữ cụ thể của luồng.

Nếu bạn không sử dụng CRT, bạn không thể tránh cuộc gọi trực tiếp tới CreateThread(). Nếu bạn sử dụng CRT, bạn phải sử dụng _beginthreadex()hoặc một số chuỗi CRT f-ns có thể không hoạt động chính xác trước VC2005.


1

CreateThread()là cuộc gọi hệ thống thẳng. Nó được triển khai trên Kernel32.dllđó, rất có thể, ứng dụng của bạn sẽ được liên kết với các lý do khác. Nó luôn có sẵn trong các hệ thống Windows hiện đại.

_beginthread()_beginthreadex()là các hàm bao bọc trong Microsoft C Runtime ( msvcrt.dll). Sự khác biệt giữa hai cuộc gọi được nêu trong tài liệu. Do đó, nó khả dụng khi Microsoft C Runtime có sẵn hoặc nếu ứng dụng của bạn được liên kết tĩnh với nó. Bạn cũng có thể sẽ liên kết với thư viện đó, trừ khi bạn mã hóa trong API Windows thuần túy (như cá nhân tôi thường làm).

Câu hỏi của bạn là một câu hỏi mạch lạc và thực sự thường xuyên. Như nhiều API, có chức năng trùng lặp và mơ hồ trong API Windows mà chúng ta phải xử lý. Tệ nhất của tất cả, các tài liệu không làm rõ vấn đề. Tôi cho rằng _beginthread()họ các hàm được tạo ra để tích hợp tốt hơn với các hàm C tiêu chuẩn khác, chẳng hạn như thao tác errno. _beginthread()do đó tích hợp tốt hơn với thời gian chạy C.

Mặc dù vậy, trừ khi bạn có lý do chính đáng để sử dụng _beginthread()hoặc _beginthreadex(), bạn nên sử dụng CreateThread(), chủ yếu là vì bạn có thể nhận được một phụ thuộc thư viện ít hơn trong tệp thực thi cuối cùng của mình (và đối với MS CRT, điều này có vấn đề một chút). Bạn cũng không có mã gói xung quanh cuộc gọi, mặc dù hiệu ứng này không đáng kể. Nói cách khác, tôi tin rằng lý do chính để gắn bó CreateThread()là không có lý do chính đáng để sử dụng _beginthreadex()để bắt đầu. Các chức năng là chính xác, hoặc gần như, giống nhau.

Một lý do chính đáng để sử dụng _beginthread() (dường như là sai) rằng các đối tượng C ++ sẽ được thư giãn / phá hủy đúng cách nếu _endthread()được gọi.


Không có cuộc gọi chức năng mơ hồ nào cả . CreateThreadlà lệnh gọi API của Windows để tạo một chuỗi. Nếu bạn đang sử dụng CRT (vì bạn đang lập trình bằng C hoặc C ++), bạn nên tạo chủ đề bằng các _beginthread[ex]cuộc gọi của CRT (cuộc gọi này CreateThreadngoài việc thực hiện khởi tạo CRT cần thiết). Sự khác biệt quan trọng nhất giữa _beginthreadvà biến thể cũ: Cái trước giữ quyền sở hữu của xử lý luồng gốc, trong khi cái sau chuyển quyền sở hữu cho người gọi.
IInspectable

Nitpick: msvcrt.dllkhông DLL C runtime! Xem blog.msdn.microsoft.com/oldnewthing/20140411-00/?p=1273
Govind Parmar

0

Các câu trả lời khác không thảo luận về ý nghĩa của việc gọi hàm thời gian chạy C bao bọc hàm Win32 API. Điều này rất quan trọng khi xem xét hành vi khóa DLL loader.

Có hay không _beginthread{ex}bất kỳ quản lý bộ nhớ sợi / sợi C đặc biệt nào như các câu trả lời khác thảo luận, nó được triển khai trong (giả sử liên kết động với thời gian chạy C) một DLL mà các quá trình có thể chưa được tải.

Nó không an toàn để gọi _beginthread*từ DllMain. Tôi đã kiểm tra điều này bằng cách viết một DLL được tải bằng tính năng "AppInit_DLLs" của Windows. Gọi _beginthreadex (...)thay vìCreateThread (...) làm RẤT NHIỀU bộ phận quan trọng của Windows ngừng hoạt động trong quá trình khởi động vì các DllMainkhóa chết điểm nhập chờ khóa bộ tải được phát hành để thực hiện một số tác vụ khởi tạo nhất định.

Ngẫu nhiên, đây cũng là lý do tại sao kernel32.dll có rất nhiều hàm chuỗi chồng chéo mà thời gian chạy C cũng làm - sử dụng các hàm từ DllMainđể tránh tình huống tương tự.


0

Nếu bạn đọc cuốn sách Gỡ lỗi ứng dụng Windows từ Jeffrey Richter, ông giải thích rằng hầu như trong tất cả các trường hợp bạn phải gọi _beginthreadexthay vì gọi CreateThread. _beginthreadchỉ là một bao bọc đơn giản hóa xung quanh _beginthreadex.

_beginthreadexkhởi tạo một số nội bộ CRT (C RunTime) nhất định mà CreateThreadAPI sẽ không làm.

Hậu quả là nếu bạn sử dụng CreateThreadAPI thay vì sử dụng _begingthreadexcác lệnh gọi đến các hàm CRT có thể gây ra sự cố không mong muốn.

Kiểm tra Tạp chí Microsoft cũ này từ Richter.


-2

Không có sự khác biệt nữa giữa cả hai.

Tất cả các nhận xét về rò rỉ bộ nhớ, v.v đều dựa trên các phiên bản <VS2005 rất cũ. Tôi đã thực hiện một số thử nghiệm căng thẳng nhiều năm trước và có thể làm sáng tỏ huyền thoại này. Ngay cả Microsoft cũng trộn các kiểu trong ví dụ của họ, hầu như không bao giờ sử dụng _beginthread.


CreateThread : "Nếu một luồng được tạo bằng CreatThread gọi CRT, CRT có thể chấm dứt quá trình trong điều kiện bộ nhớ thấp."
IInspectable

Dựa trên mức độ "yêu cầu sử dụng phiên bản đa luồng của CRT" tôi cho rằng đây là rác tài liệu vì không còn phiên bản crt đa luồng nữa và trong nhiều năm nay.
Lothar

"không còn phiên bản crt đa luồng nữa" - MSDN tuyên bố rằng "[t] anh ấy CRT đơn luồng không còn khả dụng nữa." Bạn không thể cả hai đều đúng. Tôi cũng sẽ đi với MSDN ở đây.
IInspectable

Đó là một lỗi đánh máy, tất nhiên tôi có nghĩa là một luồng đã biến mất và đa luồng đã trở thành tiêu chuẩn và những gì đã biến mất là sự phân biệt giữa việc sử dụng hay không sử dụng các luồng.
Lothar

Điều này thực sự trở nên kỳ lạ. Bây giờ bạn đang sử dụng một tuyên bố, điều đó chắc chắn là đúng ( "yêu cầu sử dụng phiên bản đa luồng của CRT" ) để tuyên bố rằng cả tuyên bố này cũng như phần còn lại của tài liệu đều rất có thể sai? Điều đó chắc chắn không đúng.
IInspectable
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.