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

网站规划的缩略图凡科互动游戏怎么玩高分

网站规划的缩略图,凡科互动游戏怎么玩高分,清远建设网站,网站的前台和后台2.1 线程的基本操作 每个程序至少有一个执行main()函数的线程#xff0c; 其他线程与主线程同时运行。 如main()函数执行完成退出一样#xff0c; 线程执行完函数也会退出。 为线程创建std::thread() 对象后#xff0c; 需要等待该线程结束。 那么#xff0c; 先启动线程。…2.1 线程的基本操作 每个程序至少有一个执行main()函数的线程 其他线程与主线程同时运行。 如main()函数执行完成退出一样 线程执行完函数也会退出。 为线程创建std::thread() 对象后 需要等待该线程结束。 那么 先启动线程。 2.1.1 启动线程 一些情况下 任务函数对象需要通过某种通讯机制进行参数的传递 或者执行一系列独立操作 通过通讯机制传递信号让线程信号停止。 先放下这些特殊情况不谈 简单来说 使用C线程库启动线程 就是构造std::thread对象: void do_some_work(); std::thread my_thread(do_some_work);这里需要包含头文件 std::thread可以通过有函数操作符类型的实例进行构造: class background_task { public:void operator()() const{do_something();do_something_else();} };background_task f; std::thread my_thread(f);代码中 提供的函数对象会复制到新线程的存储空间中 函数对象的执行和调用都在线程的内存空间中进行。 注意:当把函数对象传入线程构造函数中时如果你传递了一个临时变量 而不是一个命名的变量。 C编译器会将其解析为函数声明 而不是雷翔对象的定义。 std::thread my_thread(background_task());这相当对声明了一个名为my_thread的函数 这个函数带有一个函数(函数指针执行没有参数并返回background_task对象的函数), 返回一个std::thread对象的函数。 使用在前面命名函数对象的方式 或使用多组括号(1), 或者使用统一的初始化语法(2), 都可以避免该问题。 std::thread my_thread((background_task())); //(1) std::thread my_thread{background_task()}; //(2)Lambda表达式也能避免这个问题。 Lambda表达式允许使用一个可以捕获局部变量的局部函数(可以避免传递参数)。 之前的例子可以改写为Lambda表达式的方式: std:thread my_thread([]{do_something();do_something_else();});线程启动后是要等待线程结束 还是让其自动运行。 当std::thread()对象销毁之前还没有做出决定 程序就会终止(std::thread的析构函数会调用std::terminate())。因此 即使是有异常存在 也需要确保线程能够正确汇入(joined)或分离(detached)。 如果不等待线程汇入 就必须保证线程结束之前 访问数据的有效性。这不是一个新问题–单线程代码中 对象销毁之后再去访问 会产生未定义行为–不过 线程的生命周期增加了这个问题发生的几率。 这种情况很可能发生在线程还没借宿 函数已经退出的时候 这时线程函数还持有函数局部变量的指针或引用。 代码2.1 函数已经返回 线程依旧访问局部变量 struct func {int i;func(int i_) : i(i_) {}void operator() (){for (unsigned j0 ; j1000000 ; j){do_something(i); // (1) 潜在访问隐患空引用(dangling reference)}} };void oops() {int some_local_state0;func my_func(some_local_state);std::thread my_thread(my_func);my_thread.detach(); // (2) 不等待线程结束 } // (3) 新线程可能还在运行代码中 已经决定不等待线程(使用了detach()(2)) 所以当oops()函数执行完成时(3) 线程中的函数可能还在运行。 如果线程还在运行 就会去调用do_something(i)(1), 这时就会访问已经销毁的变量。 如同一个单线程程序–允许在函数完成后继续拥有局部变量的指针或引用。 当然 这种情况发生时 错误并不明显 会使多线程更容易出错。 运行顺序参考下表2.1。 表2.1 分离( leaving it to run)线程在局部变量销毁后 仍对该变量进行访问。 主线程新线程使用some_local_state构造my_func开启新线程my_thread启动调用func::operator()将my_thread分离执行func::operator();可能会在do_something中调用some_local_state的引用销毁some_local_state持续运行退出oops函数持续执行func::operator()可能会在do_something中调用some_local_state的引用 -- 导致未定义行为这种情况的常规处理方法将数据复制到线程中。 如果使用一个可调用的对象作为线程函数 这个对象就会复制到线程中 而后原始对象会立即销毁。 如代码2.1所示 但对于对象中包含的指针和引用还需谨慎。 使用访问局部变量的函数去创建线程是一个糟糕的注意(bad idea)。此外 可以通过join()函数来确保线程在主函数完成前结束。 2.1.2 等待线程完成 如需等待线程 需要使用join()。 将代码2.1的my_thread.detach()替换为my_thread.join(), 就可以确保局部变量在线程完成后才销毁。 因为主线程并没有做什么事 使用独立的线程去执行函数变得意义不大。 但在实际中 原始线程要么有自己的工作要做 要么会启动多个子线程来做一些有用的工作 并等待这些线程结束。   当你需要对等待中的线程有更灵活的控制时 如 看一下某个线程是否结束 或者只等待一段时间(超过时间就判定为超时)。 想要做到这些 需要使用其他机制来完成 比如条件变量和future。 调用join(), 还可以清理了线程相关的内存 这项std::thread对象将不再与已完成的线程有任何关联。 这意味着 只能对一个线程使用一次join(), 一旦使用过join(), std::thread对象就不能再次汇入了。 当对使用joinable()时 将返回flase。 2.1.3 特殊情况下的等待 如果想要分离线程 可以在线程启动后 直接使用detach()进行分离。 如果等待线程 则需要细心挑选使用join()的位置。 当在线程运行后产生的一场 会在join调用之前抛出 这样就会跳过join()。 避免应用被抛出的异常所终止。 通常 在无异常的情况下使用join()时 需要在异常处理过程中调用join() 从而避免生命周期的问题。 代码2.2 等待线程完成 struct func; // 定义在代码2.1中 void f() {int some_local_state0;func my_func(some_local_state);std::thread t(my_func);try{do_something_in_current_thread();}catch(...){t.join(); // (1)throw;}t.join(); // (2) }代码2.2中使用了try/catch块确保线程退出后函数才结束。 当函数正常退出后 会执行(2)处。 当执行过程中抛出异常 程序会执行到(1)。 如果线程在函数之前结束–就要查看是否因为线程函数使用了局部变量的引用–而后确定一下程序可能会退出的途径。有一个叫RAII(Resource Acquisition Is Initialization)的机制可以解决这个问题提供一个类 在析构函数中使用join()。 代码2.3 使用RAII等待线程完成 class thread_guard {std::thread t; public:explicit thread_guard(std::thread t_):t(t_){}~thread_guard(){if(t.joinable()) // (1){t.join(); // (2)}}thread_guard(thread_guard const)delete; // (3)thread_guard operator(thread_guard const)delete; };struct func; // 定义在代码2.1中void f() {int some_local_state0;func my_func(some_local_state);std::thread t(my_func);thread_guard g(t);do_something_in_current_thread(); } // (4)线程执行到(4)时局部对象就要被逆序销毁了。 因此 thread_guard对象g是第一个被销毁的 这时线程在析构函数中被加入(2)到原始线程中。 即使do_something_in_current_thread抛出一个异常 这个销毁依然会发生。   在thread_guard析构函数的测试中 首先判断线程是否可汇入(1)。如果可汇入 会调用join()(2)进行汇入。拷贝构造函数和拷贝复制操作标记为delete(3), 是为了不让编译器自动生成。 直接对对象进行拷贝或复制是很危险的 因为这可能会弄对已汇入的线程。 通过删除生命 任何尝试给thread_guard对象复制的操作都会引发一个编译错误。 参阅附录A的A.2节。 如果不想等待线程结束 可以分离线程 从而避免异常。 不过 这就打破了线程与std::thread对象的联系 即使线程仍然在后台运行着 分离操作也能确保在std::thread对象销毁时不调用std::terminate()。 2.1.4 后台运行线程 使用detach()会让线程在后台运行 这就意味着与主线程不能直接交互。 如果线程分离 就不可能有std::thread对象使用它 分离线程的确在后台运行 所以分离的线程不能汇入。 不过C运行库保证 当线程退出时 相关资源的能够正确回收。   分离线程通常为守护线程(daemon threads)。 UNIX中守护线程是指没有任何显示的接口 并在后台运行的线程 这种线程的特点就是长时间运行。线程的生命周期可能会从应用的起始到结束 可能会在后台监视文件系统 还有可能对缓存进行清理 或者对数据结构进行优化。另外 分离线程只能确定线程什么时候结束 发后即忘(fire and forget)的任务使用到就是分离线程。 如2.1.2节所示 调用std::thread成员函数detach成员函数detach()来分离一个线程。 之后 相应的std::thread对象与实际执行的线程无关 并且这个线程也无法汇入: std::thread t(do_background_work); t.detach(); assert(!t.joinable());为了从std::thread对象中分离线程 不能对没有执行线程的std::thread对象使用detach(), 并且要使用同样的方式进行检查–当std::thread对象使用t.joinable()返回的是true, 就可以使用t.detach()。    试想如何能让一个文字处理应用同事编辑多个文档。 无论是用户界面 还是在内部应用内部进行 都有很多的解决方法。 虽然 这些窗口看起来是完全独立的 每个窗口都有自己独立的菜单选项 但他们却运行在同一个应用实例中。 一种内部处理方式是 让每个文档处理窗口拥有自己的线程。 每个线程运行同样的代码 并个隔离不同窗口处理的数据。 如此这般 打开一个文档就要启动一个新线程。 因为是对独立文档进行操作 所以没有必要等待其他线程完成 这里就可以让文档处理窗口运行在分离线程上。 代码2.4使用分离线程处理文档 void edit_document(std::string const filename) {open_document_and_display_gui(filename);while(!done_editing()){user_command cmdget_user_input();if(cmd.typeopen_new_document){std::string const new_nameget_filename_from_user();std::thread t(edit_document,new_name); // 1t.detach(); // (2)}else{process_user_input(cmd);}} }如果用户选择打开一个新文档 需要启动一个新线程去打开新文档(1), 并分离线程(2)。 与当前线程做出的操作一样 新线程只不过是打开另一个文件而已。 所以 edit_document函数可以复用 并通过传参的形式打开新的文件。   这个例子也展示了传参启动线程的方法: 不仅可以向std::thread构造函数(1)传递函数名 还可以传递函数所需的参数(实参)。 当然 也有其他方法可以完成这项功能 比如: 使用带有数据的成员函数 代替需要传参的普通函数。 2.2 传递参数 如代码2.4所示 向可调用对象或函数传递参数很简单 只需要将这些参数作为std::thread构造函数的附加函数即可。这些参数会拷贝至新线程的内存空间中(同临时变量一样)。即使函数中的参数是引用的形式拷贝操作也会执行。 void f(int i, std::string const s); std::thread t(f, 3, hello);代码创建了一个调用(3, “hello”)的线程。 注意 函数f需要一个std::string对象作为第二个参数 但这里使用的是字符串的字面值 也就是char const*类型 线程的上下文完成字面值想std::string的转化。 需要特别注意 指向动态变量的指针作为参数的情况 代码如下: void f(int i,std::string const s); void oops(int some_param) {char buffer[1024]; //(1)sprintf(buffer, %i,some_param);std::thread t(f,3,buffer); // (2)t.detach(); }buffer(1)是一个指针变量 指向局部变量 然后此局部变量通过buffer传递到新线程中(2)。此时 函数oops可能会在buffer转换成std::string之前结束 从而导致未定义的行为。 因为 无法保证隐式转换的操作和std::thread构造函数的拷贝操作的顺序 有可能std::thread的构造函数拷贝的是转换前的变量(buffer指针)。解决方案就是在传递到std::thread构造函数之前 就将字面值转化为std::string void f(int i,std::string const s); void not_oops(int some_param) {char buffer[1024];sprintf(buffer,%i,some_param);std::thread t(f,3,std::string(buffer)); // 使用std::string避免悬空指针t.detach(); }相反的情形(期望传递一个非常量引用 但复制了整个对象)倒是不会出现 因为会出现编译错误。 比如 尝试使用线程更新引用传递的数据结构 void update_data_for_widget(widget_id w, widget_data data); // (1) void oops_again(widget_id w) {widget_data data;std::thread t(updata_data_for_widget, w, data); // (2)display_status();t.join();process_widget_data(data); }虽然updata_data_for_widget(1)的第二个参数期待传入一个引用 但std::thread的构造函数(2)并不知晓 构造函数无视函数参数类型 盲目地拷贝已提供的变量。 不过 内部代码会将拷贝的参数以右值的方式进行传递 这是为了哪些支持移动的类型 而后会尝试以右值为实参调用updata_data_for_widget。 但因为函数期望的是一个非常量引用作为参数(而非右值) 所以会在编译时出错。 对于熟悉std::bing的开发者来说 问题的解决方法很简单可以使用std::ref将参数转换成引用的形式。 因此可将线程的调用改为以下形式: std::thread t(update_data_for_widget, w, std::ref(data));这样update_data_for_widget就会收到data的引用 而非data的拷贝副本 这样代码就能顺利的通过编译了。   如果熟悉std::bind, 就应该不会对以上传参的语法感到陌生 因为std::thread构造函数和std::bind的操作在标准库中以相同的机制进行定义。 比如 你也可以传递一个成员函数指针作为线程函数 并提供一个合适的对象指针作为第一个参数 class X {public:void do_lengthy_work(); }; X my_x; std::thread t(X::do_lengthy_work, my_x); // 1另一种有趣的情形是 提供的参数仅支持移动(move)不能拷贝。 移动是指原始对象中的数据所有权转移给另一个对象 从而这些数据就不再原始对象中保存(类似于剪切)。 std::unique_ptr就是这样一种类型 这种类型为动态分配的对象提供内存自动管理机制(类似垃圾回收机制)。 同一时间内 只允许一个std::unique_ptr实例指向一个对象 并且当这个实例销毁时 指向的对象也将被删除。 移动构造函数(move constructor)和移动赋值操作符(move assignment operator)允许一个对象的所有权后 就会留下一个空指针。 使用移动操作可以将对象转换成函数可接受的实参类型 或满足函数返回值类型要求。 当原对象是临时变量时 则自动进行移动操作 但当原对象是一个命名变量 移动的时候就需啊哟使用std::move()进行显示移动。下面的代码展示了std::move的用法 展示了std::move 是如何移动态对象的所有权到线程中去的: void process_big_object(std::unique_ptrbig_object); std::unique_ptrbig_object p(new big_object); p-prepare_data(42); std::thread t(jprocess_big_object, std::move(p));通过在std::thread构造函数中执行std::move§, big_object对象的所有权首先被转移到新创建线程的内部存储中 之后再传递给process_big_object函数。   C标准线程库中和std::unique_ptr在所属权上相似的类有好几种 std::thread为其中之一。 虽然 std::thread不像std::unique_ptr能占有动态对象的所有权 但是它能占用其他资源: 每个实例都负责管理一个线程。 线程的所有权可以在多个std::thread实例中转移 这依赖于std::thread实例的可移动且不可复制性。 不可复制性表示某一时间点 一个std::thread实例只能关联一个执行线程。 可移动性是的开发者可以自己决定 哪个实例拥有线程实际执行的所有权。
http://www.w-s-a.com/news/568747/

相关文章:

  • 现在网站还用asp做男科医院哪家好一些
  • 服装设计网站素材郑州汉狮做网站网络公司
  • 宜州做网站做仿制网站
  • 中山营销型网站厦门工程建设招聘信息网站
  • 网站开发 外包空心找回微信
  • 长沙市网站推广多少钱网站开发流程图和介绍
  • 网站后缀net施工企业自建自用的工程可以不进行招标是否正确
  • 鄂尔多斯市住房和城乡建设厅网站帮别人做网站赚钱6
  • 宜选科技就是帮人做网站动漫制作专业主修课程
  • 怎么做网站免费的教程温州捷创网站建设
  • 做免费推广的网站有哪些深圳华强北最新消息
  • 电子商务网站建设规划开题报告桂林漓江景区游玩攻略
  • 程序员参与洗钱网站建设做视频网站需要多少钱
  • 网站建设背景是什么企业邮箱怎么写
  • 山东省建设资格中心网站iis wordpress安装教程
  • 做的网站 显示乱码怎么做微信小程序平台
  • 怎样建设打字网站怎样增加网站浏览量
  • 网站建设方案的征求意见网站主机免备案
  • 共享农业网站建设郑州市建网站
  • 成都网站建设四川冠辰网站建设带会员系统的网站模板
  • 水果网站建设方案书wordpress get_the_category
  • 第一ppt网站官网买域名价格
  • 网站 报价单自己做的网站如何上传
  • 天津网站建立辽宁建设工程信息网2017年定额人工费系数
  • 柳州网站优化搜索引擎优化方法案例
  • 什么网站比较少人做响应式网站开发周期
  • 公司网站欢迎语工作期间员工花钱做的网站
  • 新网站该如何做网站优化呢网络营销网站设计
  • 旅游门户网站模板下载做策划网站推广怎么写简历
  • 建设隔离变压器移动网站wordpress动态导航