python多任务编程

多任务编程

多任务

就是操作系统可以同时运⾏多个任务。打个 ⽐⽅,你⼀边在⽤浏览器上⽹,⼀边在听MP3,⼀边在⽤Word赶作业,这就是多任务,⾄少同时有3个任务正在运⾏。还有很多任务悄悄地在后台同时运 ⾏着,只是桌⾯上没有显示⽽已。
操作系统轮流让各个任务交替执⾏,每个任务执⾏0.01秒,这样反复执⾏下去。 表⾯上看,每个任务交替执⾏,但CPU的执⾏速度实在是太快了,感觉就像所有任务都在同时执⾏⼀样。

图片一

真正的并⾏执⾏多任务只能在多核CPU上实现,但是,由于任务数量远远多 于CPU的核⼼数量,所以,操作系统也会⾃动把很多任务轮流调度到每个核 ⼼上执⾏。

图片二

多进程编程

进程和程序的区别

编写完毕的代码,在没有运⾏的时候,称之为程序
正在运⾏着的代码,就成为进程

注意: 进程,除了包含代码以外,还有需要运⾏的环境等,所以和程序是有区别的

进程的五种状态

图片三

进程的创建

Python的os模块封装了常⻅的系统调⽤,其中就包括fork,可以在Python程 序中轻松创建⼦进程:

图片四

图片五

执⾏到os.fork()时,操作系统会创建⼀个新的进程复制⽗进程的所有信息到⼦进程中

普通的函数调⽤,调⽤⼀次,返回⼀次,但是fork()调⽤⼀次,返回两次

⽗进程和⼦进程都会从fork()函数中得到⼀个返回值,⼦进程返回是0,⽽⽗进程中返回⼦进程的 id号

多进程中,每个进程中所有数据(包括全局变量)都各有拥有⼀份,互 不影响

windows进程创建

Windows没有fork调⽤,由于Python是跨平台的, multiprocessing模块就是跨平台版本的多进程模块。multiprocessing模块提供了⼀个Process类来代表⼀个进程对象。

Process([group [, target [, name [, args [, kwargs]]]]]) 
	target:表示这个进程实例所调⽤对象; 
	args:表示调⽤对象的位置参数元组; 
	kwargs:表示调⽤对象的关键字参数字典; 
	name:为当前进程实例的别名; 
	group:⼤多数情况下⽤不到;

	Process类常⽤⽅法:
is_alive():	判断进程实例是否还在执⾏;
join([timeout]):	是否等待进程实例执⾏结束,或等待多少秒; 
start():		启动进程实例(创建⼦进程); 
run():		如果没有给定target参数,对这个对象调⽤start()⽅法时,
           		就将执 ⾏对象中的run()⽅法; 
terminate():	不管任务是否完成,⽴即终⽌;

Process类常⽤属性: 
	name:当前进程实例别名,默认Process-N,N为从1开始计数;
	pid:当前进程实例的PID值;

多进程编程方法:实例化

图片

图片

多进程编程方法:创建子类

图片

进程池

当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。

Pool可以提供指定数量的进程供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。

图片

图片

图片

进程间通信

图片

图片

消息队列

可以使⽤multiprocessing模块的Queue实现多进程之间的数据传递,Queue 本身是⼀个消息列队程序。

Queue.qsize():	返回当前队列包含的消息数量; 
Queue.empty():	如果队列为空,返回True,反之False ; 
Queue.full():	如果队列满了,返回True,反之False; 

Queue.get([block[, timeout]]):
获取队列中的⼀条消息,然后将其从列队中移除,block默认值为True;
Queue.get_nowait():
相当Queue.get(False);
Queue.put(item,[block[, timeout]]):
将item消息写⼊队列,block默认值 为True;
Queue.put_nowait(item):
相当Queue.put(item, False)

图片

图片

多线程编程

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
每个进程至少有一个线程,即进程本身。进程可以启动多个线程。操作系统像并行“进程”一样执行这些线程。

图片

图片

进程和线程的区别

进程是资源分配的最小单位,线程是程序执行的最小单位。
进程有自己的独立地址空间。线程是共享进程中的数据的,使用相同的地址空间.
进程之间的通信需要以通信的方式(IPC)进行。线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,难点:处理好同步与互斥。

线程的分类

有两种不同的线程:
内核线程
用户空间线程或用户线程
内核线程是操作系统的一部分,而内核中没有实现用户空间线程。

线程创建

python的thread模块是⽐较底层的模块,python的threading 模块是对thread做了⼀些包装的,可以更加⽅便的被使⽤

图片

图片

多线程程序的执⾏顺序是不确定的。
当执⾏到sleep语句时,线程将被阻塞(Blocked),到sleep结束后,线程进⼊就绪(Runnable)状态,等待调度。⽽线程调度将⾃⾏选择⼀个线程执⾏。
代码中只能保证每个线程都运⾏完整个run函数,但是线程的启动顺序、 run函数中每次循环的执⾏顺序都不能确定。

线程的状态

图片

项目案例: ip地址归属地批量查询

图片

图片

图片

图片

import requests
import json
import threading
import  time
import  random
import pymysql
def addr_sql(ip_list):
    conn = pymysql.connect(host = '172.25.254.169',user = 'root',passwd = 'westos',db = 'westos')
    cursor = conn.cursor()
    #create_sqli = 'create table ip_addr(ip varchar(10),city varchar(10),country varchar(10))'
    #cursor.execute(create_sqli)
    insert_sqli = 'insert into ip_addr(ip,city,country) values ("%s","%s","%s")'
    cursor.executemany(insert_sqli,ip_list)
    conn.commit()
    cursor.close()
    conn.close()

def get_addr(ip):
    url = "http://ip-api.com/json/%s" %(ip)
    page_request = requests.get(url).text
    time.sleep(random.random())
    dict_data = json.loads(page_request)
    print('ip(%s):%s:%s' %(ip,dict_data['city'],dict_data['country']))
    ip_list.append([ip,dict_data['city'],dict_data['country']])


if __name__ == '__main__':
    thread = []
    ip_list = []
    for i in range(20, 50):
        ip = '1.1.1.' + str(i)
        task = threading.Thread(target=get_addr,args=(ip,))
        task.start()
        thread.append(task)
    [task.join() for task in thread]
    # addr_sql(ip_list)
    get_addr('117.35.135.243')

项目案例:批量探寻未使用ip

项目描述: 如果要在本地网络中确定哪些地址处于活动状态或哪些计算机处于活动状态,则可以使用此脚本。我们将依次ping地址, 每次都要等几秒钟才能返回值。这可以在Python中编程,在IP地址的地址范围内有一个for循环和一个os.popen(“ping -q -c2”+ ip)。
项目瓶颈: 没有线程的解决方案效率非常低,因为脚本必须等待每次ping。

from threading import Thread
class GetHostAliveThread(Thread):

    def __init__(self,ip):
        super(GetHostAliveThread,self).__init__()
        self.ip = ip


    def run(self):
        import os
        cmd = 'ping -cl -wl %s &> /dev/null'%(self.ip)
        result = os.system(cmd)
        if result != 0:
            print('no %s'%(self.ip))

if __name__ == '__main__':
    for i in range(1,255):
        ip = '172.25.254.'+ str(i)
        thread = GetHostAliveThread(ip)
        thread.start()

线程共享全局变量问题

GIL(global interpreter lock): python解释器中任意时刻都只有一个线程在执行;
Python代码的执行由Python 虚拟机(也叫解释器主循环,CPython版本)来控制,Python 在设计之初就考虑到要在解释器的主循环中,同时只有一个线程在执行,即在任意时刻,只有一个线程在解释器中运行。对Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

图片

图片

线程同步

线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作.
同步就是协同步调,按预定的先后次序进⾏运⾏。如:你说完,我再说。
"同"字从字⾯上容易理解为⼀起动作 其实不是,
"同"字应是指协同、协助、互相配合。
图片

图片

图片

图片

死锁

在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时 等待对⽅的资源,就会造成死锁。

图片

图片

协程

协程,又称微线程,纤程。英文名Coroutine。协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

图片

执行效率极高,因为子程序切换(函数)不是线程切换,由程序自身控制,
没有切换线程的开销。所以与多线程相比,线程的数量越多,协程性能的优势越明显。
不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在控制共享资源时也不需要加锁,因此执行效率高很多。

协程实现

图片

图片

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值