python进程线程协程区别_Python3多线程与协程

python中的多线程非常的常用,之前一直糊里糊涂地使用,没有一些系统性的概念,记录一下~

0x001 多线程的优势:可将长时间占用的程序放到后台

可能会加速程序执行速度

能够实现一些类似同步执行的效果

0x002 线程线程是OS的执行单元

每个独立的线程都有一个程序运行的入口、顺序执行序列和程序出口,线程不能离开程序独立执行。

每个线程都有自己唯一的一组CPU寄存器(上下文),反映了线程上次运行时该CPU寄存器的状态。线程中指令指针和堆栈指针寄存器非常重要,线程在进程中得到上下文,这些地址用于标志拥有线程的进程地址空间中的内存

线程可被抢占

线程退让

0x01 分类:名字

说明

用户线程

不需内核支持而在用户程序中实现的线程

内核线程

操作系统内核创建和撤销

0x02 Py3中的threading模块:方法

说明

threading.current_thread()

返回当前的线程变量

threading.enumerate()

返回正在运行线程的list(启动后,结束前)

threading.active_count

等于len(threading.enumerate())

提供了 Thread类,所以还可以

方法

说明

run()

表示线程活动的方法

start()

启动线程活动

join([time])

阻塞式等待线程终止

isAlive()

线程是否活动

getName()

返回线程名

setName()

设置线程名

# -*- coding:utf-8 -*-

# 多线程

# DYBOY

# time:2019-3-10 09:37:48

import threading

import time

def printNum(endNum):

for i in range(1,endNum+1):

print(i, time.time())

# 创建线程

t = threading.Thread(target=printNum, name='printThread', args=(10,))

t.start()

t.join()

print("线程%s结束" % threading.current_thread().name)

0x003 多线程&多进程&线程锁多进程中同一变量,各自有拷贝到自己的进程中,互不影响,多线程中,变量由多个线程共享,因此多线程中变量的同步就需要的到控制

lock = threading.Lock()

def runThread():

for i in range(1000):

lock.acquire()

try:

#....执行函数

finally:

lock.release()

# -*- coding:utf-8 -*-

# 多线程

# DYBOY

# time:2019-3-10 09:37:48

import threading

import time

money = 0

lock = threading.Lock()

def chaneMoney(num):

global money

money += num

money -= num

def runThread(n):

for i in range(1000):

lock.acquire()

try:

chaneMoney(n)

finally:

lock.release()

t1 = threading.Thread(target=runThread, args=(100,))

t2 = threading.Thread(target=runThread, args=(50,))

t1.start()

t2.start()

t1.join()

t2.join()

print("余额:",money)

ps: 在实际的运行中,发现似乎线程锁没有起到作用,在线程中的join() 方法似乎是有影响的,

join():阻塞当前进程/线程,直到调用join方法的那个进程执行完,再继续执行当前进程。相当于线程守护,直到调用join()方法的线程执行完毕,才将控制权交给主进程。

0x04 问题?

从上,看到多线程中为了保证数据的一致性,使用了线程锁来实现类似同步的功能,然而这样反而多了获取锁和释放锁的步骤,所以在我看来。线程也没有加快程序的运行时间。

一个程序从执行到结束,首先会创建一个主进程,os的执行单元是线程,一个进程有至少一个或多个线程来实现其功能,在线程的创建和上下文切换是一个比较大的开销,提升多线程的优势就需要从其中来考虑:

无锁并发(减少数据关联度,更合理优化的实现方式)

减少并发(线程不能无限制的多)

减少上下文切换的开销(协程)

0x05 协程函数调用的时候,是使用栈的方式,比如A调用B,B调用C,C执行返回给B,B执行完后返回给A,是一个压栈出栈的过程

子程序(函数),总是一个入口,一次返回,调用的顺序永远如此,所以如果有比较频繁的函数调用,那么就用较多的上下文切换时间,利用协程(微线程)可以较好解决这个问题。

协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。(多进程)

# -*- coding:utf-8 -*-

# 协程 gevent

# DYBOY

# time:2019-3-10 09:37:48

# description: 下载图片到本地(普通版本)

# from gevent import monkey

# monkey.patch_all()

import requests,time,json

def get_save_pic(picUrl, name):

img = requests.get(picUrl)

with open('pic/'+name,'wb') as f:

f.write(img.content)

return None

if __name__ == '__main__':

sT = time.time()

jsonData = requests.get('http://img.top15.cn/piclist.php')

jsonData = json.loads(jsonData.text)

imgs = jsonData['data']

for img in imgs:

get_save_pic(img[0], img[1])

print("Success", time.time() - sT)

网络效果好的时候:

6661013-4738660f282dcebc.jpg

# -*- coding:utf-8 -*-

# 协程 gevent

# DYBOY

# time:2019-3-10 09:37:48

# description: 下载图片到本地

from gevent import monkey

monkey.patch_all()

import gevent,requests,time,json

def get_save_pic(picUrl, name):

img = requests.get(picUrl)

with open('pic/'+name,'wb') as f:

f.write(img.content)

return None

if __name__ == '__main__':

startTime = time.time()

jsonData = requests.get('http://img.top15.cn/piclist.php')

jsonData = json.loads(jsonData.text)

imgs = jsonData['data']

targetLists = []

for img in imgs:

targetLists.append(gevent.spawn(get_save_pic, img[0], img[1]))

gevent.joinall(targetLists)

print("Success!", time.time()-startTime)

# 不知道什么原因,没有输出,但是从执行的结果上来看

# 最后,所有图片同时在文件夹生成,非常迅速

2019-3-10 22:05:04 在命令行下可正常执行!

6661013-584df826d563fc8d.jpg

从肉眼可见的角度来看,还是协程的效果更好(在数据量不大下,感觉比较而得出的结论还是不是很有说服力,在数据量大的情况下,线程不能无限增加,协程的效果表现更优异,再加上多进程应该就更NICE了)。

0x06 总结

本次探究的是多线程与协程的区别,多线程不能无限创建,所以有的时候创建多线程在生产环境下是不可行的,在爬虫下载图片这部分是可以使用多线程去下载,多线程其实也是一个等待执行的过程,其与协程的差别主要是在上下文切换上,协程减少了上下文切换的时间,是程序自己控制的,而多线程的上下文切换是需要系统调用会耗费更多的时间,本次例子实现中使用了monkey这个模块,还不清楚其中遇到的输出问题,继续探究!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值