一步一步写线程之一简单的开始

一、多线程

多线程不管是在前面的文章分析中还是在网上还是大书籍上,学习C/C++多线程编程是无法绕过的,即使使用别人封装好的框架,包括使用STL的一些库,如果仅仅是简单的应用,可能也就过去了。不过,稍微复杂的一些应用,其实就需要对多线程的应用有深层次的理解。
尤其在使用一些封装库时,特别要注意库的一些注意事项,往往这些注意点是应用BUG的重要原因。比如std::async中std::future返回值如果控制不好,异步就可能变成了同步。所以说多线程要想写好,不但要会用一些库一些API,更要懂得这些库的底层原理,直到这个库调用的不同的平台,封装的是哪个OS的系统函数。如果再展开到并发和并行,更得需要了解相关OS的相关进程线程的调度知识等等。这些都是写多线程的开发者需要一步步学习的目标。不过在这里,只就时论事,只讲多线程,与OS相关的知识,用到就展开分析一下。
这个系列更侧重于线程的应用,而非基础知识的普及,所以如果在示例的一些源码代码中不清楚的基础的用法,需要去查一下前面的多线程的基础知识的文章或者自己行在网上查找一些相关知识。

二、线程的运用

多线程的应用其实是一个非常重要且普遍的应用。协程的编程目前看来应该是被排除到了更高的应用层上,也就是说,想用好协程,更多的是纯上层应用了。线程被迁移到了一个中间层的库应用。说得再深入一些,协程的普及,可能会让开发者写并发变得简单但同样大幅降低了技术门槛,这意味着什么,以前不明白的,这两年应该都懂了。
多线程的应用一般会和异步IO共同使用,有的人在网上说多线程和异步有什么不同,这就说汽车和钢铁有什么不同一样,这本来就是两回事。说多了,扯回来。
多线程的应用非常广泛,图像处理、数据读写、IO通信和时间控制等等,都可以用到。但正如已经分析过的一样,用的方法不一样,那么产生的结果也不一样。单纯的多线程读写操作不涉及到数据互斥没有什么难度。举个简单的例子,写十个线程,读十个不同的文件,这和写普通的非线程代码没有本质不同,可能只是套上了十个线程的壳子。但是如果这十个线程需要同时把读到数据写到一个缓冲区内,根据不同情况来覆盖或者追加相关数据,这就需要谨慎的处理了。否则数据很可能不是丢失就是多存储了。
另外,如果只有五个线程来处理十个文件呢?如果这十个文件大小不一,有的非常大,有的非常小?那么小文件读取完成后,这个线程不就空闲了么?多线程不还是单线程么。再细分一下,大文件可不可以切成段,在别的线程读完成文件后去读这些分段的大文件来加速读取的速度呢?这都是多线程需要解决的现实问题。
很多问题,在学习多线程编程时,是很难想到的或者说遇到实际问题不知道怎么下手,这其实就是学无法致用的一个典型的表现。在这个系列中,就把这些多线程的用法与实际的情况结合起来,进行一一的分析。

三、例程

做为简单的开始,本篇不做多么复杂的例程,只是把刚刚提到std::async遇到的同步问题展现一下以及相关的处理办法:

#include <iostream>       
#include <future>         
#include <chrono>

int getData(int x) {
   
	std::cout << "async thread run start!"<<std::endl;
	std::this_thread::sleep_for(std::chrono::milliseconds(6000));
	std::cout << "async thread run end!" << std::endl;

	return x;
}

int main()
{
   
	std::async(std::launch::async, getData, 100);

	std::cout << "go to ..."<<std::endl;

	return 0;
}

运行结果:

async thread run start!
async thread run end!
go to ...

你会发现运行的结果并不是期房的两个线程并行执行,而执行完async中的线程,才回头执行主线程的函数。什么原因呢,在std::async中有下面的话:
“If the std::future obtained from std::async is not moved from or bound to a reference, the destructor of the std::future will block at the end of the full expression until the asynchronous operation completes, essentially making code such as the following synchronous”
在std::future的析构函数中:
“these actions will not block for the shared state to become ready, except that they may block if all of the following are true:
the shared state was created by a call to std::async,
the shared state is not yet ready, and
the current object was the last reference to the shared state.”
在下面还有一句话,意思是说只在运行策略为“ std::launch::async ”才会产生这种情况。
所以通过上述两个说明就明白了,在std::async中,std::future的临时变量会在没有被移动或者引用情况下一直阻塞到异步计算完成,所以这句话也就给了解决方法:

auto d = std::async(std::launch::async, getData, 100);

只需增加上面的返回值处理即可,延长一下std::future的生命周期至期望的位置即可。用别人的东西,就得服别人的套路,一个不小心,就会吃亏上当。不过话又说回来,想省事,吃点亏也应该,对吧。

四、总结

从简单开始,不忘初心,朝着实际应用不断的前进。多线程编程属于那种入门容易,写好难,精通更难的一种编程技术,真正的生产上,大牛们被憋住当场打脸的情况也不少见。总之一句话,多看书,多实践,多应用。小心写代码,防御性编程,少引入BUG比如事后调试定位要更容易做到一些。

相关推荐

  1. 线之一简单开始

    2023-12-10 01:20:05       52 阅读
  2. 线之四简单线

    2023-12-10 01:20:05       40 阅读
  3. 线之十memory_order应用

    2023-12-10 01:20:05       31 阅读
  4. 线之十一线池应用内存池

    2023-12-10 01:20:05       33 阅读
  5. 线之十五协

    2023-12-10 01:20:05       28 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-10 01:20:05       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-10 01:20:05       100 阅读
  3. 在Django里面运行非项目文件

    2023-12-10 01:20:05       82 阅读
  4. Python语言-面向对象

    2023-12-10 01:20:05       91 阅读

热门阅读

  1. 如何设计自动完成系统

    2023-12-10 01:20:05       61 阅读
  2. PCL 三维点云中求解圆的三维方程

    2023-12-10 01:20:05       59 阅读
  3. FPGA | Verilog基础语法

    2023-12-10 01:20:05       68 阅读
  4. Vue笔记(四)路由

    2023-12-10 01:20:05       52 阅读
  5. 请简要介绍一下HTML的发展史?

    2023-12-10 01:20:05       50 阅读
  6. 区间价值 --- 题解--动态规划

    2023-12-10 01:20:05       60 阅读
  7. spring 两个service相互引用,会有循环依赖吗

    2023-12-10 01:20:05       56 阅读
  8. Lintcode 1160 · Campus Bikes (三元组排序好题)

    2023-12-10 01:20:05       45 阅读
  9. ca单点登录

    2023-12-10 01:20:05       67 阅读