您的位置:首页 > 编程语言 > Python开发

python入门三(多线程,多进程)

2017-07-05 10:31 281 查看
python语言提供了对多线程、多进程的支持,下面来看看相关知识

一、python多线程

1、python多线程模块

   python的多线程提供了两个模块即thread和threading,threading这个模块提供了更多的功能和更高级的使用方式所以thread我们就不学了,只学threading模块!

2、该模块常用属性、方法

threading模块的Thread 类,这个类的对象就是一个线程,这个类有很多属性和方法,比如name(线程名)、daemo(是否要设置为守护线程)

Thread类的相关方法:start()启动线程、join()在该线程执行完之前主线程的阻塞isAlivel()线程是否结束、isDaemon()判断是否为守护线程

3、python多线程demo

import threading
from time import  sleep,ctime
loops =[2,4]
def loop(nloop,nsec):
print ("start nloop %s at %s"%(nloop,ctime()))
sleep(nsec)
print("loop done at "+str(ctime()))
def main():
print ("start time:"+str(ctime()))
threads =[]
nloops =range(len(loops))
for i in nloops:
t = threading.Thread(target=loop,args=(nloops,loops[i],))
threads.append(t)
for i in nloops:
threads[i].start()

for i in nloops:
threads[i].join()
print("all threads end")
if __name__=="__main__":
main()
我们使用的是threading模块,所以导入threading而不是thread
定义一个列表,两个元素
loop()方法,该方法又两个参数即一个常量和休眠秒数,用方法参数和ctime()给占位符赋值
sleep(nsec)休眠时间
main()方法中定义了一个列表threads[]
获取一个随机数nloops
遍历创建线程对象并给这个线程绑定任务以及需要的参数,当然还可以设置名称name =如果不设置有默认值
调用threading 模块的Thread对象,其中target的值就是给该线程绑定的方法(任务),args=()参数传的是元组
使用列表的append方法将线程对象添加到列表中
线程对象调用start()方法启动线程
线程对象调用join()方法阻塞主线程直到该线程结束


4、python线程池demo

from concurrent.futures import ThreadPoolExecutor
import time
def return_future_result(message):
time.sleep(2)
return message
pool = ThreadPoolExecutor(max_workers=2)
future1 = pool.submit(return_future_result, ("hello"))
future2 = pool.submit(return_future_result, ("world"))
print(future1.done())
time.sleep(3)
print(future2.done())
print(future1.result())
print(future2.result())
这里使用了线程池来执行任务,如果任务个数大于线程池的最大数则任务会等待线程否则不用等待!

这里对多线程只是简单的了解,没有吧所有的实现方式都列出来,如果之前学过java的多线程你会发现python的多线程中能看到java的所有多线程的影子,我只是说类名称什么很像!!!

二、python多进程

在说进程前先来看看python中的解释器全局锁(GIL)

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

全局解释器锁GIL设计理念与限制

GIL的设计简化了CPython的实现,使得对象模型,包括关键的内建类型如字典,都是隐含可以并发访问的。锁住全局解释器使得比较容易的实现对多线程的支持,但也损失了多处理器主机的并行计算能力。

但是,不论标准的,还是第三方的扩展模块,都被设计成在进行密集计算任务是,释放GIL。

还有,就是在做I/O操作时,GIL总是会被释放。对所有面向I/O 的(会调用内建的操作系统C 代码的)程序来说,GIL 会在这个I/O 调用之前被释放,以允许其它的线程在这个线程等待I/O 的时候运行。如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100 次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过 sys.setcheckinterval 来调整)如果某线程并未使用很多I/O 操作,它会在自己的时间片内一直占用处理器(和GIL)。也就是说,I/O 密集型的Python 程序比计算密集型的程序更能充分利用多线程环境的好处。

ok这样大体上了解了python多线程,在网上也有说python的多线程就是鸡肋……我们不管那些,通过上面我们知道,python的多线程确实不太适合用在cpu密集型的应用中,其实python就不适合用在cpu密集型的应用中。

但python给我们还提供了多进程,能用多进程的我们就可以使用多进行来运行这样就能更好的利用计算机资源

1、python进程的模块

python 多进程有三个模块包括subprocess、multiprocessing、concurrent.futures 这里我们也就简单学习一下后两个的使用方式

python进程的创建:Process([group [, target [, name [, args [, kwargs]]]]]),target表示调用对象,args表示调用对象的位置参数元组。kwargs表示调用对象的字典。name为别名。group实质上不使用。

2、该模块的常用属性、方法

方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()启动某个进程。

属性:authkey、daemon(要通过start()设置)、exitcode(进程在运行时为None、如果为–N,表示被信号N结束)、name、pid。其中daemon是父进程终止后自动终止,且自己不能产生新进程,必须在start()之前设置。
3、python进程demo

(1)、单一进程:

import multiprocessing
import time

def worker(interval):
n = 5
while n > 0:
print("The time is {0}".format(time.ctime()))
time.sleep(interval)
n -= 1

if __name__ == "__main__":
p = multiprocessing.Process(target = worker, args = (3,))
p.start()
print (p.pid:"+str(p.pid))
print (p.name:"+str(p.name))
print (p.is_alive:"+str(p.is_alive()))
(2)、多进程:(这个直接copy的)

import multiprocessing
import time

def worker_1(interval):
print ("worker_1")
time.sleep(interval)
print ("end worker_1")

def worker_2(interval):
print ("worker_2")
time.sleep(interval)
print ("end worker_2")

def worker_3(interval):
print ("worker_3")
time.sleep(interval)
print ("end worker_3")

if __name__ == "__main__":
p1 = multiprocessing.Process(target = worker_1, args = (2,))
p2 = multiprocessing.Process(target = worker_2, args = (3,))
p3 = multiprocessing.Process(target = worker_3, args = (4,))

p1.start()
p2.start()
p3.start()

print("The number of CPU is:" + str(multiprocessing.cpu_count()))
for p in multiprocessing.active_children():
print("child   p.name:" + p.name + "\tp.id" + str(p.pid))
print ("END!!!!!!!!!!!!!!!!!")


4、python进程池:

在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。

(1)、非阻塞进程池:

import multiprocessing
import time

def func(msg):
print ("msg:"+str(msg))
time.sleep(3)
print ("end")

if __name__ == "__main__":
pool = multiprocessing.Pool(processes = 3)
for i in range(4):
msg = "hello %d" %(i)
pool.apply_async(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去

print ("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
pool.close()
pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
print ("Sub-process(es) done.")

apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,
apply(func[, args[, kwds]])是阻塞的(理解区别,看例1例2结果区别)
close()关闭pool,使其不在接受新的任务。
terminate()    结束工作进程,不在处理未完成的任务。
join()    主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。


(2)、阻塞进程池:

import multiprocessing
import time

def func(msg):
print ("msg:"+str(msg))
time.sleep(3)
print ("end")

if __name__ == "__main__":
pool = multiprocessing.Pool(processes = 3)
for i in range(4):
msg = "hello %d" %(i)
pool.apply(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去

print ("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
pool.close()
pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
print ("Sub-process(es) done.")


从python3.2开始concurrent.futures被纳入了标准库,所以我们可以使用进程池来实现,具体demo据不列出来了,上班真苦逼,没时间学……

可以参考这篇文章:http://www.tuicool.com/articles/rq6bMf

总结:

通过上面的简单学习能简单实用python多线程,多进程了,但通过学习发现,在api的使用上多线程和多进程很相似,不管是使用就得线程池进程池还是新的你可能也会发现他们的这些使用方式和java的多线程很相似(使用上相似,底层实现不知道),所以还是那句话触类旁通。

注:本人学习python只是为了写一些简单的脚本,所以不会深入学习相关知识点更不会全面深入学习,所以想深入学习的请忽略!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: