多线程下载一个大文件的速度更快的真正原因是什么?

引言
  日常工作中,大家应该经常遇到要下载资源的场景,下载资源时,有时网络很给力,一会儿就下载成功了,有时下载很慢,几十分钟后都还在下载中,甚至更过分的是下载好长时间后直接来个下载失败。好不惹人生气。
当你在遇到这样的下载场景时,有没有思考过到底是什么原因影响着文件资源的下载速度呢?

实时网络带宽
  决定用户下载大文件速度快慢的终极因素,在于用户下载进程实时抢占网络带宽的大小。其它的因素与它相比,可以忽略不计。

  如果用户进程实时抢占的带宽 = 实时网络可用带宽,则在最理想的状态下,用户下载进程100%利用网络带宽,无论该下载进程是单线程(Thread)的还是多线程的,下载速度几乎没有任何区别。【因为此时没有别的进程使用网络带宽】。

  但是在现实中实际是用户进程实时抢占的带宽 <= 实时网络可用带宽!因为实时网络带宽每一刻都是在变化的,那它是怎么变化的呢?因为TCP流量控制。

TCP流量控制
  传统的TCP流量探测机制有一个非常致命的缺陷:一旦检测到有丢包,立马将发送速率降为1/2。降速1/2后,如果没有丢包,将会在1/2速率的基础上,按照固定的增长值(线性增长),加大发送的速率。接下来就会一直按照这个节奏到达丢包的那一刻(实时可用带宽)为止。如果下一个检测周期依然有丢包现象,会在当前1/2速率的基础上继续降速1/2。循环往复,直到文件下载结束。

  很显然指数级的降速、但是线性的增速;这最后造成的结果就是真实的传输速率远远小于实时可用带宽。

多线程下载
  多线程下载时,由于多个线程在竞争实时可用带宽。尽管多线程逻辑上是并行的,但其实还是按时序的串行处理。所以每个线程处于的阶段并不一致。并且带宽资源是固定的。

  比如使用3个线程来进行下载,因为处于不同的阶段,有的线程因为丢包直接降速1/2,有的线程处于线性增长阶段。通过多个线程的加权平均,最后得到的下载曲线是一条平滑的曲线,且这条曲线大多数应该处于单线程下载速率的上方。这也是为什么多线程下载大文件的速度更快的原因了。

最后
  最后,如果我问你写一个程序来求1亿以内素数个数,在求素数的算法已经确定的情况下,用什么样的方式花的时间更少呢?我想答案应该很清楚吧。

欢迎关注【无量测试之道】公众号,回复【领取资源】
Python编程学习资源干货、
Python+Appium框架APP的UI自动化、
Python+Selenium框架Web的UI自动化、
Python+Unittest框架API自动化、
资源和代码 免费送啦~
文章下方有公众号二维码,可直接微信扫一扫关注即可。

备注:我的个人公众号已正式开通,致力于测试技术的分享,包含:大数据测试、功能测试,测试开发,API接口自动化、测试运维、UI自动化测试等,微信搜索公众号:“无量测试之道”,或扫描下方二维码:

在这里插入图片描述
添加关注,让我们一起共同成长!
————————————————
版权声明:本文为CSDN博主「Wu_Candy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41754309/article/details/118972104

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于文件读取操作,使用线程可以提高读取速度,但具体效果取决于多种因素,如硬件配置、文件大小、文件类型、读取方式等等。在C++中,可以使用线程库std::thread来创建线程并执行文件读取操作。需要注意的是,线程操作需要考虑线程安全问题,如文件读写冲突等。 在Go中,可以使用goroutine和channel来实现多线程读取大文件。具体实现方式可以参考以下示例代码: ```go func main() { file, err := os.Open("largefile.txt") if err != nil { log.Fatal(err) } defer file.Close() // 创建一个channel用于传输文件内容 content := make(chan []byte) // 启动多个goroutine并发读取文件 for i := 0; i < runtime.NumCPU(); i++ { go readFromFile(file, content) } // 从channel中读取文件内容 var result []byte for i := 0; i < runtime.NumCPU(); i++ { result = append(result, <-content...) } // 处理文件内容 fmt.Println(string(result)) } // 读取文件内容并传输到channel中 func readFromFile(file *os.File, content chan []byte) { buffer := make([]byte, 1024) var result []byte for { n, err := file.Read(buffer) if err != nil { if err == io.EOF { content <- result return } log.Fatal(err) } result = append(result, buffer[:n]...) } } ``` 以上代码使用goroutine并发读取文件,每个goroutine读取文件的一部分,并将读取的内容传输到channel中。最后,主goroutine从channel中读取所有内容并进行处理。这种方式可以利用多核CPU的优势,并且避免了线程安全问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值