当前位置: 首页 > news >正文

合肥网站建设 卫来网络天津网站页面设计

合肥网站建设 卫来网络,天津网站页面设计,开封网站建设报价,教学app制作C进阶专栏#xff1a;http://t.csdnimg.cn/HGkeZ 相关系列文章#xff1a; std::thread使用及实现原理精讲(全) 有了std::thread#xff0c;为什么还需要引入std::jthread#xff1f; 目录 1.windows创建线程 2.linux创建线程 3._beginthread小融合 4.CreateThread与_… C进阶专栏http://t.csdnimg.cn/HGkeZ  相关系列文章 std::thread使用及实现原理精讲(全) 有了std::thread为什么还需要引入std::jthread 目录 1.windows创建线程 2.linux创建线程 3._beginthread小融合 4.CreateThread与_beginthread的异同         5.std::thread大融合 5.1.std::thread的使用 5.1.1.线程的创建 5.1.2.线程的退出 5.1.3.异常情况下等待线程完成 5.1.4.用std::ref向线程传递引用参数 5.2.std::thread实现原理 5.2.1.线程参数退变 5.2.2.与_beginthreadex的关系 5.2.3.std::thread构造 5.2.4.成员变量_Thr 6.总结 1.windows创建线程 windows一般采用CreateThread创建线程它的声明如下 HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全属性DWORD dwStackSize, //线程初始栈大小LPTHREAD_START_ROUTINE lpStartAddress, //线程函数入口通常用线程函数名LPVOID lpParameter, //给新线程函数传递参数DWORD dwCreationFlags, //设置新线程附加标记为0时新线程立即运行LPDWORD lpThreadld, //用来返回新线程的线程ID如果不感兴趣设为NULL ; //Windows 系统提供的线程创建函数 参数和返回值含义 1.参数IpThreadAttributes 指定线程安全属性当该参数位NULL时线程获取默认安全描述符 2.参数 dwStackSize 指定线程堆栈的初始大小以字节为单位。如果该值为0则新线程使用可执行文件的默认大小 3.参数 lpStartAddress 指定由线程执行的自定义函数的指针; 4.参数 lpParameter 指定自定义函数需要的参数 5.参数 dwCreationFlags 指定线程创建后所处的状态 6.参数 lpThreadID 指定接收线程标识符的变量的指针若该参数为NULL则不需返回该标识符 如果新线程创建成功则返回值为新线程的句柄若不成功则返回NULL。 示例如下 #include windows.h #include tchar.h #include strsafe.h#define MAX_THREADS 3 #define BUF_SIZE 255DWORD WINAPI MyThreadFunction( LPVOID lpParam ); void ErrorHandler(LPTSTR lpszFunction);// Sample custom data structure for threads to use. // This is passed by void pointer so it can be any data type // that can be passed using a single void pointer (LPVOID). typedef struct MyData {int val1;int val2; } MYDATA, *PMYDATA;int _tmain() {PMYDATA pDataArray[MAX_THREADS];DWORD dwThreadIdArray[MAX_THREADS];HANDLE hThreadArray[MAX_THREADS]; // Create MAX_THREADS worker threads.for( int i0; iMAX_THREADS; i ){// Allocate memory for thread data.pDataArray[i] (PMYDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,sizeof(MYDATA));if( pDataArray[i] NULL ){// If the array allocation fails, the system is out of memory// so there is no point in trying to print an error message.// Just terminate execution.ExitProcess(2);}// Generate unique data for each thread to work with.pDataArray[i]-val1 i;pDataArray[i]-val2 i100;// Create the thread to begin execution on its own.hThreadArray[i] CreateThread( NULL, // default security attributes0, // use default stack size MyThreadFunction, // thread function namepDataArray[i], // argument to thread function 0, // use default creation flags dwThreadIdArray[i]); // returns the thread identifier // Check the return value for success.// If CreateThread fails, terminate execution. // This will automatically clean up threads and memory. if (hThreadArray[i] NULL) {ErrorHandler(TEXT(CreateThread));ExitProcess(3);}} // End of main thread creation loop.// Wait until all threads have terminated.WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);// Close all thread handles and free memory allocations.for(int i0; iMAX_THREADS; i){CloseHandle(hThreadArray[i]);if(pDataArray[i] ! NULL){HeapFree(GetProcessHeap(), 0, pDataArray[i]);pDataArray[i] NULL; // Ensure address is not reused.}}return 0; }DWORD WINAPI MyThreadFunction( LPVOID lpParam ) { HANDLE hStdout;PMYDATA pDataArray;TCHAR msgBuf[BUF_SIZE];size_t cchStringSize;DWORD dwChars;// Make sure there is a console to receive output results. hStdout GetStdHandle(STD_OUTPUT_HANDLE);if( hStdout INVALID_HANDLE_VALUE )return 1;// Cast the parameter to the correct data type.// The pointer is known to be valid because // it was checked for NULL before the thread was created.pDataArray (PMYDATA)lpParam;// Print the parameter values using thread-safe functions.StringCchPrintf(msgBuf, BUF_SIZE, TEXT(Parameters %d, %d\n), pDataArray-val1, pDataArray-val2); StringCchLength(msgBuf, BUF_SIZE, cchStringSize);WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, dwChars, NULL);return 0; } void ErrorHandler(LPTSTR lpszFunction) { // Retrieve the system error message for the last-error code.LPVOID lpMsgBuf;LPVOID lpDisplayBuf;DWORD dw GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS,NULL,dw,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) lpMsgBuf,0, NULL );// Display the error message.lpDisplayBuf (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR) lpMsgBuf) lstrlen((LPCTSTR) lpszFunction) 40) * sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR),TEXT(%s failed with error %d: %s), lpszFunction, dw, lpMsgBuf); MessageBox(NULL, (LPCTSTR) lpDisplayBuf, TEXT(Error), MB_OK); // Free error-handling buffer allocations.LocalFree(lpMsgBuf);LocalFree(lpDisplayBuf); } 先是用CreateThread创建3个线程3个子线程与主线程并驾齐驱再用WaitForMultipleObjects无限等待3个子线程的退出最后释放资源。 与线程有关的其它函数如下 2.linux创建线程 linux系统一般用函数pthread_create创建线程函数定义如下 int pthread_create(pthread_t *tidp,const pthread_attr_t *attr, void *(*start_rtn)(void*),void *arg); 参数和返回值含义 1.参数tidp事先创建好的pthread_t类型的参数。成功时tidp指向的内存单元被设置为新创建线程的线程ID。 2.参数attr用于定制各种不同的线程属性。APUE的12.3节讨论了线程属性。通常直接设为NULL。 3.参数start_rtn新创建线程从此函数开始运行。无参数是arg设为NULL即可。 4.参数argstart_rtn函数的参数。无参数时设为NULL即可。有参数时输入参数的地址。当多于一个参数时应当使用结构体传入。 如果成功返回0否则返回错误码。 示例代码如下 #includestdio.h #includestdlib.h #includeassert.h #includestring.h #includeunistd.h#includepthread.hint main() {printf(main start\n);pthread_t id;int res pthread_create(id,NULL,fun,NULL);assert(res 0);//之后并发运行int i 0; for(; i 5; i){printf(main running\n);sleep(1);}char *s NULL;pthread_join(id,(void **)s);printf(join : s %s\n,s);exit(0); }//定义线程函数 void* fun(void *arg) {printf(fun start\n);int i 0;for(; i 10;i){printf(fun running\n);sleep(1);}printf(fun over\n);pthread_exit(fun over);//将该字符常量返回给主线程 } 此时主线程完成五次输出就会等待子线程结束阻塞等待子线程结束后最后主线程打印joins fun over。 3._beginthread小融合 函数定义如下 //线程的开始 unsigned long _beginthread(void(_cdecl *start_address)(void *), //声明为void (*start_address)(void *)形式unsigned stack_size, //是线程堆栈大小一般默认为0void *arglist //向线程传递的参数一般为结构体 );unsigned long _beginthreadex( //推荐使用void *security, //安全属性NULL表示默认安全性unsigned stack_size, //是线程堆栈大小一般默认为0unsigned(_stdcall *start_address)(void *), //声明为unsigned(*start_address)(void *)形式void *argilist, //向线程传递的参数一般为结构体unsigned initflag, //新线程的初始状态0表示立即执行CREATE_SUSPEND表示创建后挂起。unsigned *thrdaddr //该变量存放线程标识符它是CreateThread函数中的线程ID。 ); //创建成功条件下的将线程句柄转化为unsigned long型返回创建失败条件下返回0//线程的结束 //释放线程空间、释放线程TLS空间、调用ExiteThread结束线程。 void _endthread(void); // retval:设定的线程结束码与ExiteThread函数的参数功能一样 //其实这个函数释放线程TLS空间再调用ExiteThread函数但没有释放线程空间。 void _endthreadex(unsigned retval); 两组函数都是用来创建和结束线程的。这两对函数的不同点如下 1.从形式上开_beginthreadex()更像CreateThread()。_beginthreadex()比_beginthread()多3个参数intiflagsecurity和threadaddr。 2.两种创建方式的线程函数不同。_beginthreadex()的线程函数必须调用_stdcall调用方式而且必须返回一个unsigned int型的退出码。 3._beginthreadex()在创建线程失败时返回0而_beginthread()在创建线程失败时返回-1。这一点是在检查返回结果是必须注意的。 4.如果是调用_beginthread()创建线程并相应地调用_endthread()结束线程时系统自动关闭线程句柄而调用_beginthreadx()创建线程并相应地调用_endthreadx()结束线程时系统不能自动关闭线程句柄。因此调用_beginthreadx()创建线程还需程序员自己关闭线程句柄以清除线程的地址空间。 示例代码如下 // crt_begthrdex.cpp // compile with: /MT #include windows.h #include stdio.h #include process.hunsigned Counter; unsigned __stdcall SecondThreadFunc( void* pArguments ) {printf( In second thread...\n );while ( Counter 1000000 )Counter;_endthreadex( 0 );return 0; }int main() {HANDLE hThread;unsigned threadID;printf( Creating second thread...\n );// Create the second thread.hThread (HANDLE)_beginthreadex( NULL, 0, SecondThreadFunc, NULL, 0, threadID );// Wait until second thread terminates. If you comment out the line// below, Counter will not be correct because the thread has not// terminated, and Counter most likely has not been incremented to// 1000000 yet.WaitForSingleObject( hThread, INFINITE );printf( Counter should be 1000000; it is- %d\n, Counter );// Destroy the thread object.CloseHandle( hThread ); } 4.CreateThread与_beginthread的异同         用使用 CreateThread 是 Windows 的 API 函数只需要和 Kernel32.lib 库链接。 用使用 _beginthread 和 _beginthreadex应用必须和 CRT(C RunTime) 库链接。 所以一个线程要使用静态 CRT(C RunTime)的库函数必须使用 _beginthread 和 _beginthreadex 函数。 不过在 _beginthread 和 _beginthreadex 函数的内部实现代码中调用的是 CreateThread 函数来实现的这很显然嘛CRT 库也是要运行在Windows上。 直接在CreateThread API创建的线程中使用sprintfmallocstrcat等涉及CRT存储堆操作的CRT库函数是很危险的容易造成线程的意外中止。 在使用_beginthread和_beginthreadex创建的线程中可以安全的使用CRT函数但是必须在线程结束的时候相应的调用_endthread或_endthreadex。 5.std::thread大融合 std::thread是C11推出的标准线程类利用它就可以非常简单的创建一个线程而且也不区分哪个操作系统。真正实现了线程创建的大统一。 5.1.std::thread的使用 std::thread提供的接口有 函数名含义join阻塞等待到该线程结束。detach将线程从父进程分离无法再通过 thread 对象对其进行操作生命周期也脱离父进程最终由操作系统进行资源回收。joinable检查线程是否可被阻塞等待。get_id获取该线程的唯一标识符。swap与指定 thread 对象进行互换操作。native_handle获取该线程的句柄。hardware_concurrency [static]返回逻辑处理器数量。 5.1.1.线程的创建 线程创建支持的可调用对象有C语言函数、仿函数、类成员函数、lambda函数等。示例代码如下 #include chrono #include iostream #include thread #include utility//C语言函数 void f1(int n) {for (int i 0; i 5; i){std::cout 正在执行线程1\n;n;std::this_thread::sleep_for(std::chrono::milliseconds(10));} }void f2(int n) {for (int i 0; i 5; i){std::cout 正在执行线程2\n;n;std::this_thread::sleep_for(std::chrono::milliseconds(10));} }//类成员函数 class foo { public:void bar(){for (int i 0; i 5; i){std::cout 正在执行线程3\n;n;std::this_thread::sleep_for(std::chrono::milliseconds(10));}}int n 0; }; //仿函数 class baz { public:void operator()(){for (int i 0; i 5; i){std::cout 正在执行线程4\n;n;std::this_thread::sleep_for(std::chrono::milliseconds(10));}}int n 0; };int main() {int n 0;foo f;baz b;std::thread t1; // t1 不是线程std::thread t2(f1, n 1); // 按值传递std::thread t3(f2, std::ref(n)); // 按引用传递std::thread t4(std::move(t3)); // t4 现在运行 f2()。t3 不再是线程std::thread t5(foo::bar, f); // t5 在对象 f 上运行 foo::bar()std::thread t6(b); // t6 在对象 b 的副本上运行 baz::operator()std::thread t7([](){ int x 0; std::this_thread::sleep_for(std::chrono::milliseconds(6));});t2.join();t4.join();t5.join();t6.join();t7.join();std::cout n 的最终值是 n \n;std::cout f.n (foo::n) 的最终值是 f.n \n;std::cout b.n (baz::n) 的最终值是 b.n \n; } 输出 正在执行线程1 正在执行线程2 正在执行线程3 正在执行线程4 正在执行线程3 正在执行线程1 正在执行线程2 正在执行线程4 正在执行线程2 正在执行线程3 正在执行线程1 正在执行线程4 正在执行线程3 正在执行线程2 正在执行线程1 正在执行线程4 正在执行线程3 正在执行线程1 正在执行线程2 正在执行线程4 n 的最终值是 5 f.n (foo::n) 的最终值是 5 b.n (baz::n) 的最终值是 0 5.1.2.线程的退出 当线程启动后一定要在和线程相关联的thread销毁前确定以何种方式等待线程执行结束。 C11有两种方式来等待线程结束 detach方式启动的线程自主在后台运行当前的代码继续往下执行不等待新线程结束。前面代码所使用的就是这种方式。 调用detach表示thread对象和其表示的线程完全分离分离之后的线程是不在受约束和管制会单独执行直到执行完毕释放资源可以看做是一个daemon线程分离之后thread对象不再表示任何线程分离之后joinable() false即使还在执行 示例代码如下 #include iostream #include thread #include chrono using namespace std::chrono_literals;void foo() {std::this_thread::sleep_for(500ms); }int main() {std::cout std::boolalpha;std::thread t;std::cout before starting, joinable: t.joinable() \n;t std::thread{foo};std::cout after starting, joinable: t.joinable() \n;t.join();std::cout after joining, joinable: t.joinable() \n;t std::thread{foo};t.detach();std::cout after detaching, joinable: t.joinable() \n;std::this_thread::sleep_for(1500ms); } join方式等待启动的线程完成才会继续往下执行。假如前面的代码使用这种方式其输出就会0,1,2,3因为每次都是前一个线程输出完成了才会进行下一个循环启动下一个新线程。 只有处于活动状态线程才能调用join可以通过joinable()函数检查;joinable() true表示当前线程是活动线程才可以调用join函数默认构造函数创建的对象是joinable() false;join只能被调用一次之后joinable就会变为false表示线程执行完毕调用 ternimate()的线程必须是 joinable() false;如果线程不调用join()函数即使执行完毕也是一个活动线程即joinable() true依然可以调用join()函数 无论在何种情形一定要在thread销毁前调用t.join或者t.detach来决定线程以何种方式运行。 当使用join方式时会阻塞当前代码等待线程完成退出后才会继续向下执行 而使用detach方式则不会对当前代码造成影响当前代码继续向下执行创建的新线程同时并发执行这时候需要特别注意创建的新线程对当前作用域的变量的使用创建新线程的作用域结束后有可能线程仍然在执行这时局部变量随着作用域的完成都已销毁如果线程继续使用局部变量的引用或者指针会出现意想不到的错误并且这种错误很难排查。例如 auto fn [](const int *a) {for (int i 0; i 10; i){cout *a endl;} };[fn] {int a 1010;thread t(fn, a);t.detach(); }(); 在lambda表达式中使用fn启动了一个新的线程在装个新的线程中使用了局部变量a的指针并且将该线程的运行方式设置为detach。这样在lamb表达式执行结束后变量a被销毁但是在后台运行的线程仍然在使用已销毁变量a的指针这样就可能会导致不正确的结果出现。 所以在以detach的方式执行线程时要将线程访问的局部数据复制到线程的空间使用值传递一定要确保线程没有使用局部变量的引用或者指针除非你能肯定该线程会在局部作用域结束前执行结束。 当然使用join方式的话就不会出现这种问题它会在作用域结束前完成退出。 5.1.3.异常情况下等待线程完成 当决定以detach方式让线程在后台运行时可以在创建thread的实例后立即调用detach这样线程就会后thread的实例分离即使出现了异常thread的实例被销毁仍然能保证线程在后台运行。 但线程以join方式运行时需要在主线程的合适位置调用join方法如果调用join前出现了异常thread被销毁线程就会被异常所终结。为了避免异常将线程终结或者由于某些原因例如线程访问了局部变量就要保证线程一定要在函数退出前完成就要保证要在函数退出前调用join。 void func() {thread t([]{cout hello C 11 endl;});try{do_something_else();}catch (...){t.join();throw;}t.join(); } 上面代码能够保证在正常或者异常的情况下都会调用join方法这样线程一定会在函数func退出前完成。但是使用这种方法不但代码冗长而且会出现一些作用域的问题并不是一个很好的解决方法。 一种比较好的方法是资源获取即初始化RAII,Resource Acquisition Is Initialization)该方法提供一个类在析构函数中调用join。 C惯用法之RAII思想: 资源管理-CSDN博客 class thread_guard {thread t; public :explicit thread_guard(thread _t) :t(_t){}~thread_guard(){if (t.joinable())t.join();}thread_guard(const thread_guard) delete;thread_guard operator(const thread_guard) delete; };void func(){thread t([]{cout Hello thread endl ;});thread_guard g(t); } 无论是何种情况当函数退出时局部变量g调用其析构函数销毁从而能够保证join一定会被调用。 5.1.4.用std::ref向线程传递引用参数 向线程调用的函数传递参数也是很简单的只需要在构造thread的实例时依次传入即可。例如 void func(int *a,int n){}int buffer[10]; thread t(func,buffer,10); t.join(); 需要注意的是默认的会将传递的参数以拷贝的方式复制到线程空间即使参数的类型是引用。例如 void func(int a,const string str); thread t(func,3,hello); func的第二个参数是string 而传入的是一个字符串字面量。该字面量以const char*类型传入线程空间后在**线程的空间内转换为string**。 如果在线程中使用引用来更新对象时就需要注意了。默认的是将对象拷贝到线程空间其引用的是拷贝的线程空间的对象而不是初始希望改变的对象。如下 class _tagNode { public:int a;int b; };void func(_tagNode node) {node.a 10;node.b 20; }void f() {_tagNode node;thread t(func, node);t.join();cout node.a endl ;cout node.b endl ; } 在线程内将对象的字段a和b设置为新的值但是在线程调用结束后这两个字段的值并不会改变。这样由于引用的实际上是局部变量node的一个拷贝而不是node本身。在将对象传入线程的时候调用std::ref将node的引用传入线程而不是一个拷贝。例如 thread t(func,std::ref(node)); 也可以使用类的成员函数作为线程函数示例如下 class _tagNode{public:void do_some_work(int a); }; _tagNode node;thread t(_tagNode::do_some_work, node,20); 上面创建的线程会调用node.do_some_work(20)第三个参数为成员函数的第一个参数以此类推。 5.2.std::thread实现原理 剖析其源码是了解其机理的最好方法std::thread的部分源码(VS2019)整理如下 class thread { // class for observing and managing threads public:class id;using native_handle_type void*;thread() noexcept : _Thr{} {}private: #if _HAS_CXX20friend jthread; #endif // _HAS_CXX20template class _Tuple, size_t... _Indicesstatic unsigned int __stdcall _Invoke(void* _RawVals) noexcept /* terminates */ {// adapt invoke of users callable object to _beginthreadexs thread procedureconst unique_ptr_Tuple _FnVals(static_cast_Tuple*(_RawVals));_Tuple _Tup *_FnVals;_STD invoke(_STD move(_STD get_Indices(_Tup))...);_Cnd_do_broadcast_at_thread_exit(); // TRANSITION, ABIreturn 0;}template class _Tuple, size_t... _Indices_NODISCARD static constexpr auto _Get_invoke(index_sequence_Indices...) noexcept {return _Invoke_Tuple, _Indices...;}template class _Fn, class... _Argsvoid _Start(_Fn _Fx, _Args... _Ax) {using _Tuple tupledecay_t_Fn, decay_t_Args...;auto _Decay_copied _STD make_unique_Tuple(_STD forward_Fn(_Fx), _STD forward_Args(_Ax)...);constexpr auto _Invoker_proc _Get_invoke_Tuple(make_index_sequence1 sizeof...(_Args){});#pragma warning(push) #pragma warning(disable : 5039) // pointer or reference to potentially throwing function passed to// extern C function under -EHc. Undefined behavior may occur// if this function throws an exception. (/Wall)_Thr._Hnd reinterpret_castvoid*(_CSTD _beginthreadex(nullptr, 0, _Invoker_proc, _Decay_copied.get(), 0, _Thr._Id)); #pragma warning(pop)if (_Thr._Hnd) { // ownership transferred to the thread(void) _Decay_copied.release();} else { // failed to start thread_Thr._Id 0;_Throw_Cpp_error(_RESOURCE_UNAVAILABLE_TRY_AGAIN);}}public:template class _Fn, class... _Args, enable_if_t!is_same_v_Remove_cvref_t_Fn, thread, int 0_NODISCARD_CTOR explicit thread(_Fn _Fx, _Args... _Ax) {_Start(_STD forward_Fn(_Fx), _STD forward_Args(_Ax)...);}~thread() noexcept {if (joinable()) {_STD terminate();}}thread(thread _Other) noexcept : _Thr(_STD exchange(_Other._Thr, {})) {}thread operator(thread _Other) noexcept {if (joinable()) {_STD terminate();}_Thr _STD exchange(_Other._Thr, {});return *this;}thread(const thread) delete;thread operator(const thread) delete;void swap(thread _Other) noexcept {_STD swap(_Thr, _Other._Thr);}_NODISCARD bool joinable() const noexcept {return _Thr._Id ! 0;}void join() {if (!joinable()) {_Throw_Cpp_error(_INVALID_ARGUMENT);}if (_Thr._Id _Thrd_id()) {_Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR);}if (_Thrd_join(_Thr, nullptr) ! _Thrd_success) {_Throw_Cpp_error(_NO_SUCH_PROCESS);}_Thr {};}void detach() {if (!joinable()) {_Throw_Cpp_error(_INVALID_ARGUMENT);}_Check_C_return(_Thrd_detach(_Thr));_Thr {};}_NODISCARD id get_id() const noexcept;_NODISCARD static unsigned int hardware_concurrency() noexcept {return _Thrd_hardware_concurrency();}_NODISCARD native_handle_type native_handle() { // return Win32 HANDLE as void *return _Thr._Hnd;}private:_Thrd_t _Thr; }; 由以上代码可知 5.2.1.线程参数退变 C之std::decay_std::decty-CSDN博客 从源代码中的这行代码 using _Tuple tupledecay_t_Fn, decay_t_Args...; 可以看出传入的参数通过decay_t退变左值引用和右值引用都被擦除类型变为右值了所以想要通过传入参数返回值的方式要特别注意了。从这里就不难看出在5.1.4的章节中传入引用去线程值没有返回的原因了。 5.2.2.与_beginthreadex的关系 在源代码的_Start函数中很清晰的看到std::thread的的实现也是调用_beginthreadex函数创建线程的。 5.2.3.std::thread构造 C17之std::invoke: 使用和原理探究(全)_c新特性 invoke-CSDN博客 1.我们知道_begintrheadex中的线程函数形如 unsigned (_stdcall *start_address)(void *); 再看一下源码中_Invoke函数 template class _Tuple, size_t... _Indices static unsigned int __stdcall _Invoke(void* _RawVals) noexcept; 完全一样。 2.利用std::tuple实现参数的传递 C之std::tuple(一) : 使用精讲(全) C之std::tuple(二) : 揭秘底层实现原理 首先通过传入的参数构造出_Tuple 再利用make_index_sequence产生序列依次获取_Tuple的参数最后调用std::invoke实现函数的调用。 C14之std::index_sequence和std::make_index_sequence_std::make_index_sequence4 的递归展开过程-CSDN博客 5.2.4.成员变量_Thr 定义如下 struct _Thrd_t { // thread identifier for Win32void* _Hnd; // Win32 HANDLE_Thrd_id_t _Id; }; 两个成员变量一个是线程的ID一个是线程的句柄。在windows环境下_Hnd就是CreateThread的返回值_Id就是CreateThread函数的最后一个参数。 通过上面几个方面的分析std::thread实现也不过如此不过要真正理解它的实现还需要好好理解make_index_sequence、index_sequence、invoke等知识。 6.总结 线程创建和销毁是昂贵的操作应尽量避免频繁创建和销毁线程。 线程间共享数据时要确保数据访问的线程安全性。 尽量避免在多个线程中访问和修改全局变量或静态变量除非这些变量是线程安全的。 使用 std::thread 时要确保在程序结束前对所有线程调用 join() 或 detach()以避免资源泄漏。 总之std::thread 为 C 提供了强大而灵活的多线程支持使得开发者能够更容易地编写并行程序。然而多线程编程也带来了额外的复杂性和挑战需要开发者仔细考虑线程间的数据共享和同步问题。
http://www.pierceye.com/news/83634/

相关文章:

  • 兄弟网站制作网站建设客户问到的问题
  • 做编程的 网站有哪些内容可信网站认证查询
  • 内容电商的网站如何做做平面的公司网站
  • 在线做网站视频在线观看招聘58同城招人
  • 池州网站建设开发企业邮箱下载
  • 中国娱乐设计网站官网flow创意博客wordpress主题
  • 网站建设及推广好做吗wordpress 留言墙插件
  • 晋城网站开发怎样申请免费网站
  • php网站搭建环境专做畜牧招聘网站的
  • 百度提交网站收录地址网站如何做子域名
  • 哈尔滨做网站哪里好广州建网站费用
  • 均安网站制作it建设人才网
  • app那个网站开发比较好网络平台怎么投诉
  • 网站建设个人接单手机版网站优化
  • 网站主页模板 优帮云青岛网站优化联系方式
  • 建设信用卡积分兑换商城网站自己公司网站设计
  • 微信公众号可以自动导入wordpresswordpress+极致优化
  • 浙江建设工程造价信息网站建设银行吴中支行网站
  • 网站建设要那些收费项vps做网站
  • 合肥建网站的公司如何设计中文网站
  • 本地网站做淘宝客网站开发属于无形资产
  • 无锡网站制作启扁平化网站 源代码
  • 网站建设的技术风险asp网站开发 知识
  • 去生活服务性的网站做php好吗etc工程承包模式
  • 展示型网站建设流程方案晋中建设集团网站
  • 网站上线倒计时html5模板沈阳大型网站设计公司
  • 分类网站推广费用多少网页模板素材大全
  • 如何进行网站宣传推广全国十大跨境电商公司排名
  • 卫浴毛巾架网站建设wordpress自用主题
  • 网站开发课程学习报告wordpress悬浮搜索