江宁区建设工程局网站云南网络营销公司
2026/2/14 11:25:49 网站建设 项目流程
江宁区建设工程局网站,云南网络营销公司,帝国音乐网站怎么做数据表,wordpress阅读全文文章目录一、前言1.1 什么是并发1.2 为什么使用并发1.3 并发与C多线程二、线程基础2.1 发起线程2.2 等待线程完成2.3 lambda表达式传递2.4 在后台运行线程2.5 向线程传递参数一、前言 1.1 什么是并发 同一个系统中#xff0c;多个独立活动同时进行#xff0c;而非依次进行。…文章目录一、前言1.1 什么是并发1.2 为什么使用并发1.3 并发与C多线程二、线程基础2.1 发起线程2.2 等待线程完成2.3 lambda表达式传递2.4 在后台运行线程2.5 向线程传递参数一、前言1.1 什么是并发同一个系统中多个独立活动同时进行而非依次进行。举个生活中的例子你一边煮饭一边洗菜虽然你的手不能同时做两件事但你可以一会儿切菜、一会儿看锅整体上两项任务都在推进——这就是并发。并发的方式有两种1多进程并发。例如将一个应用软件拆分成多个独立进程同时运行它们都只含单一线程非常类似于同时运行浏览器和文字处理软件2多线程并发。线程非常像轻量级进程每个线程都独立运行并能各自执行不同的指令序列注意并发 ≠ 并行并发多个任务交替执行可能在单核 CPU 上通过时间片轮转实现。并行多个任务真正同时执行通常需要多核 CPU。1.2 为什么使用并发为分离关注点而并发归类相关代码隔离无关代码使程序更易于理解和测试因此所含缺陷很可能更少。例如一个GUI应用中主线程负责响应用户界面事件点击、拖拽等必须保持高响应性。工作线程执行耗时操作如文件读写、网络请求、复杂计算为性能而并发现代 CPU 通常有多个核心单线程程序只能利用一个核心而并发程序可以将任务拆分在多个核心上真正并行执行从而缩短总执行时间。再者很多程序性能瓶颈不在 CPU而在 I/O 操作如磁盘读写、网络请求。这些操作往往需要等待外部设备响应在此期间 CPU 是空闲的。通过并发一个线程等待 I/O 时其他线程可以继续工作系统整体吞吐量显著提升1.3 并发与C多线程C98 / C03没有线程的年代 标准中无任何线程概念C98 和 C03 标准完全没有定义线程、锁、原子操作等并发原语。所有多线程编程必须依赖操作系统 APIWindowsCreateThread, CriticalSectionPOSIXLinux/macOSpthread_create, pthread_mutex_t代码不可移植且容易出错如忘记释放锁、资源泄漏。⚠️ 内存模型缺失C98 没有明确定义内存模型即多个线程如何观察共享内存的修改顺序。编译器优化如指令重排可能导致多线程程序行为不可预测。即使使用 volatile也无法保证原子性或同步。里程碑C11随着C11标准的发布上述种种弊端被一扫而空。C标准库不仅规定了内存模型可以区分不同线程还扩增了新类分别用于线程管控、保护共享数据、同步线程间操作、以及底层原子操作等。C14进一步增添了对并发和并行的支持具体而言是引入了一种用于保护共享数据的新互斥。C17则增添了一系列适合新手的并行算法函数。这两版标准都强化了C的核心和标准程序库的其他部分简化了多线程代码的编写二、线程基础2.1 发起线程线程通过构建std::thread对象而启动该对象指明线程要运行的任务。最简单的任务就是运行一个普通函数返回空也不接收参数。函数在自己的线程上运行等它一返回线程即随之终止。#includethreadvoidthead_work1(){std::couthello thread std::endl;}// 通过()初始化并启动一个线程std::threadt1(thead_work1);2.2 等待线程完成当我们启动一个线程后线程可能没有立即执行如果在局部作用域启动了一个线程或者main函数中很可能子线程没运行就被回收了回收时会调用线程的析构函数执行terminate操作。所以为了防止主线程退出或者局部作用域结束导致子线程被析构的情况我们可以通过join让主线程等待子线程启动运行子线程运行结束后主线程再运行。#includethreadvoidthead_work1(){std::couthello thread std::endl;}intmain(){std::threadt1(thead_work1);ti.join();// 使用joinreturn0;}2.3 lambda表达式传递std::threadt4([](std::string str){std::coutstr is strstd::endl;},hellostr);t4.join();2.4 在后台运行线程调用std::thread对象的成员函数detach()会令线程在后台运行遂无法与之直接通信。假若线程被分离就无法等待它完结也不可能获得与它关联的std::thread对象因而无法汇合该线程。然而分离的线程确实仍在后台运行其归属权和控制权都转移给C运行时库由此保证一旦线程退出与之关联的资源都会被正确回收。std::threadt(do_background_work);t.detach();assert(!t.joinable());假设我们有一个程序希望启动一个后台线程持续写日志而主线程继续做其他事情不需要等待日志线程结束:#includeiostream#includethread#includechrono#includefstreamvoidbackground_logger(){std::ofstreamlog(app.log);intcount0;while(count10){logLog entry #count\n;log.flush();// 确保立即写入std::this_thread::sleep_for(std::chrono::seconds(1));}logLogger finished.\n;// 函数返回线程自然结束}intmain(){std::coutMain: Starting background logger...\n;std::threadlogger_thread(background_logger);// 将线程分离让它在后台独立运行logger_thread.detach();// 主线程继续工作std::this_thread::sleep_for(std::chrono::milliseconds(3500));std::coutMain: Doing other work...\n;std::this_thread::sleep_for(std::chrono::milliseconds(8000));std::coutMain: Exiting program.\n;// 注意如果主线程在此退出而 logger_thread 还没结束// 程序会终止后台线程也会被强制杀死}detach() 后主线程不等待主线程打印完 “Exiting program.” 就结束不会等日志线程写完 10 条日志。如果主线程结束整个进程退出所有线程都会被操作系统强制终止。所以上例中 app.log 可能只写了 34 行而不是完整的 10 行问题说明访问已销毁的栈变量如果线程函数捕获了主线程的局部变量尤其是引用或指针而主线程已退出会导致悬空指针无法处理异常detached 线程中的未捕获异常会导致std::terminate()且无法被主线程感知。资源泄漏风险如果线程持有资源如文件句柄、锁提前终止可能导致资源未释放。2.5 向线程传递参数当线程要调用的回调函数参数为引用类型时需要将参数显示转化为引用对象传递给线程的构造函数如果采用如下调用会编译失败voidproducer_thread(ThreadSafeQueueinttsq){for(inti0;i100;i)tsq.push(i);}voidrun(){ThreadSafeQueueinttsq;std::threadt_pro(producer_thread,tsq);}即使函数change_param的参数为int类型我们传递给t2的构造函数为some_param,也不会达到在change_param函数内部修改关联到外部some_param的效果。因为some_param在传递给thread的构造函数后会转变为右值保存右值传递给一个左值引用会出问题所以编译出了问题。需要使用std::refvoidrun(){ThreadSafeQueueinttsq;std::threadt_pro(producer_thread,std::ref(tsq));}有时候传递给线程的参数是独占的所谓独占就是不支持拷贝赋值和构造但是我们可以通过 std::move 的方式将参数的所有权转移给线程如下voidprocess_big_object(std::unique_ptrbig_object);std::unique_ptrbig_objectp(newbig_object);p-prepare_data(42);std::threadt(process_big_object,std::move(p));在调用std::thread的构造函数时依据std::move§所指定的操作big_object对象的归属权会发生转移先进入新创建的线程的内部存储空间再转移给process_big_object()函数

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询