热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python多线程下载编程_Python线程进阶篇|多线程下载网页

Python的并发编程前面只讲了进程和线程的入门篇,线程里面还有很多有用而好玩的招式,我们还没有讲,我一直主张学一门语言一定要有兴趣&#x

d70b69244775

Python的并发编程前面只讲了进程和线程的入门篇,线程里面还有很多有用而好玩的招式,我们还没有讲,我一直主张学一门语言一定要有兴趣,带着玩Python的心态去学,会轻松很多,打个比方打扫卫生和打羽毛球,可能都很累,为啥你打球就很high,而在家打扫卫生就很累,因为你enjoy打球哈哈,好言归正传,今天我们就来讲讲多线程编程~~

要点:

线程的同步

线程的锁

线程的通讯

1.线程如何同步

1).若主程序创建了一个线程之后,启动线程start().线程的一个关键特性是每个线程都是独立运行且状态不可预测

我们希望线程启动运行后,线程内部运行到某种情况的时候,主程序才执行

比如程序中的其他线程需 要通过判断某个线程的状态来确定自己下一步的操作

这时线程同步问题就会变得非常棘手,怎么破,很简单,利用线程threading.Event(),来设置信号量来控制

d70b69244775

>>

Start thread1...

1

2

3

Start to run in main function

4

5

6

主程序创建一个Event()对象my_event,把my_event传到子线程里面

在子线程里面设置my_event.set()也就是说子线程运行到i==4时把信号量置为True而外边的主函数my_event.wait()一直阻塞在那里,傻傻的等着信号量的状态

一旦set()了意味着信号量为True了,外边主函数就可以开始行动了

2).同样我们假设有2个子线程,那两个子线程之间的同步也可以用Event来搞定

d70b69244775

>>

Start thread1...

1

2

3

4

Ok,we can start thread2...

5

6

线程thread1和线程wait_for_thread1通过Event()对象来设置信号量,通过这个信号量来进行两个子线程的同步

2. 线程的锁

在多线程中,由于所有的变量都由所有线程共享,所以在运行的过程中,任何一个变量都可以被任何一个线程修改

因此,线程之间共享数据会有risk,很可能跑着跑着,数据就被别人改了,所以我们需要加锁来控制

举一个非常经典的例子,就是银行存取款的,相信很多同学都听说过这个例子,在传统的c++,java里面也大量用这个例子来说明锁的重要性,我们来看一下

1).假设银行存款有1000元,有两个线程对这笔钱进行操作

我再存1块钱,然后再取1块钱

我再存10块钱,然后再取10块钱

理论上讲无论多少次,这笔钱应该还是1000块,但是当两个线程同事进行大量频繁的操作的时候,诶情况就有变化了,钱多了,或者少了,不信你看:

d70b69244775

>>

1097

[Finished in0.3s]

哇多了97块钱,真是好事啊,哈哈,银行肯定不乐意了,要找原因了

11).我们在算钱的时候,做了两步

第一步:balance=balance+n

第二步:balance=balance-n

12).底层操作系统在执行的时候会分成好几步去执行,python是有C写的,C又会变成汇编语言去执行指令,所以当两个线程频繁的操作的时候,中间的指令会被打断,从而导致多个线程把同一个变量的内容改乱了(当然我们是希望钱变多哈哈)

2).所以一般对于多线程处理核心的数据,都是要加锁的

就是让某一个时刻,只能有一个线程改这个变量,让这个线程执行完了,释放锁,再让下一个线程执行,保证核心数据不会被改乱了~~比如我们执行10,100万次存取,都不会出差错

d70b69244775

>>

1000

[Finished in1.1s]

创建一个threading.Lock()对象,在处理核心数据操作的时候,先锁起来,用完再释放锁,看现在反复操作100000次,钱还是1000,一分没有少,大家有没有发现一个问题,这个是有代价的,就是运行开销很大,原来是0.3s运行完,现在要1.1s才能搞定

3. 线程之间的通信

好比我有一个工作要做,一个线程要干6分钟,那么我创建3个线程,同时去处理这些事情,是不是只要2分钟.效率高了许多.

那么怎么样才能让这些线程之间实现安全的通信或者交换数据呢

Python里面有一个高效安全的Queue模块,我们主需要把数据塞进队列,它就会被所有的线程共享

然后用put()或者get()操作来给队列添加或者移除元素

我们举一个多线程爬网页的例子吧

d70b69244775

1).首先创建一个DownloadThread类,这个类继承threading.Thread

2).类的初始化函数,留一个接口,把queue带进去,这样所有的线程都可以共享这个queue

3).类的run函数,设计成死循环,不断的从队列里面取数据,一旦这个线程把从这个队列里面的数据取完了,就调用queue.task_done()表示我已经干完了

4).类的download_file函数,其实就是具体去执行下载网页的工作,因为爬虫后面再讲,所以我这边只用了很简单的urllib.urlopen()函数,简单获取网页而已

接着看main函数

d70b69244775

>>

Thread-3:begin dowbload http://wiki.python.org/moin/WebProgramming...

Thread-1:begin dowbload https://wiki.python.org/moin/WebFrameworks...

Thread-2:begin dowbload https://wiki.python.org/moin/BeginnersGuide...

Thread-2 download completed

Thread-2:begin dowbload https://wiki.python.org/moin/PythonBooks...

Thread-1 download completed

Thread-1:begin dowbload https://wiki.python.org/moin/PythonEvents...

Thread-2 download completed

Thread-1 download completed

Thread-3 download completed

Cost time:0:00:14.604000

1).main函数里面我们设定了有5个网页要爬

2).启动3个线程,并且设定为守护线程,就是当main()函数一结束,所有的子线程全部结束

3),把5个网页(也可以认为是5个要完成的task),这里是要爬网页,以后你可以变成其他数据结构,或者直接封装成类然后塞进queue里面,让这3个线程去共享完成.

4).queue会阻塞在哪里,等到所以的线程完成那一刻

5).大家看Thread2完成了之后,又会接着去爬下一个网页(真是个勤快的孩子),Thread1也是完成之后接着去爬下一个,看起来Thread3执行的网页爬的最慢

6).最后计算一下时间,大家改一下代码,可以比较一下一个线程爬5个网页和3个线程同时去快多少(大概是3倍)

补充说一下,这个爬网页urlopen()没有加任何异常保护,当然在实际操作中是肯定要保护,并且当出现异常的时候要设置一个flag来通知线程终止的.

需要源码后台留言



推荐阅读
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 深入理解线程、进程、多线程、线程池
    本文以QT的方式来走进线程池的应用、线程、进程、线程池、线程锁、互斥量、信号量、线程同步等的详解,一文让你小白变大神!为什么要使用多线程、线程锁、互斥量、信号量?为什么需要线程 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 代理模式的详细介绍及应用场景
    代理模式是一种在软件开发中常用的设计模式,通过在客户端和目标对象之间增加一层中间层,让代理对象代替目标对象进行访问,从而简化系统的复杂性。代理模式可以根据不同的使用目的分为远程代理、虚拟代理、Copy-on-Write代理、保护代理、防火墙代理、智能引用代理和Cache代理等几种。本文将详细介绍代理模式的原理和应用场景。 ... [详细]
author-avatar
搞笑宠物图片
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有