您的位置:首页 > 其它

线程、进程、协程

2016-06-22 08:18 435 查看
线程:

线程可以理解成轻量的进程,实际在linux两者几乎没有区别,唯一的区别是线程并不产生新的地址空间和资源。

Threading用于提供线程的相关操作,线程是应用程序中工作的最小单元。

import threading
import time

def show(arg):
time.sleep(1)
print('thread'+str(arg))

for i in range(10):
t = threading.Thread(target=show, args=(i,))
t.start()


上述代码创建了10个前台线程,然后控制器就交给了cpu,CPU根据内部算法进行调度,

更多的方法:

start 线程准备就绪,等待CPU调度

setName  为线程设置名字

getName  获取线程名称

setDaemon  设置后台线程或者前台线程(默认False) 

    如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止

    如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止

join   逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义

run  线程被cpu调度后自动执行线程对象的run方法

线程锁:

由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。所以,可能出现如下问题:

from multiprocessing import Pool, TimeoutError
import time
import os

def f(x):
return x*x

if __name__ == '__main__':
# 创建4个进程
with Pool(processes=4) as pool:

# 打印 "[0, 1, 4,..., 81]"
print(pool.map(f, range(10)))

# 使用任意顺序输出相同的数字,
for i in pool.imap_unordered(f, range(10)):
print(i)

# 异步执行"f(20)"
res = pool.apply_async(f, (20,))      # 只运行一个进程
print(res.get(timeout=1))             # 输出 "400"

# 异步执行 "os.getpid()"
res = pool.apply_async(os.getpid, ()) # 只运行一个进程
print(res.get(timeout=1))             # 输出进程的 PID

# 运行多个异步执行可能会使用多个进程
multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]
print([res.get(timeout=1) for res in multiple_results])

# 是一个进程睡10秒
res = pool.apply_async(time.sleep, (10,))
try:
print(res.get(timeout=1))
except TimeoutError:
print("发现一个 multiprocessing.TimeoutError异常")

print("目前,池中还有其他的工作")

# 退出with块中已经停止的池
print("Now the pool is closed and no longer available")


进程池官方版

class multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])


processes :使用的工作进程的数量,如果processes是None那么使用 os.cpu_count()返回的数量。

initializer: 如果initializer是None,那么每一个工作进程在开始的时候会调用initializer(*initargs)。

maxtasksperchild:工作进程退出之前可以完成的任务数,完成后用一个心的工作进程来替代原进程,来让闲置的资源被释放。maxtasksperchild默认是None,意味着只要Pool存在工作进程就会一直存活。

context: 用在制定工作进程启动时的上下文,一般使用 multiprocessing.Pool() 或者一个context对象的Pool()方法来创建一个池,两种方法都适当的设置了context注意:Pool对象的方法只可以被创建pool的进程所调用。进程池的方法

apply(func[, args[, kwds]]) :使用arg和kwds参数调用func函数,结果返回前会一直阻塞,由于这个原因,apply_async()更适合并发执行,另外,func函数仅被pool中的一个进程运行。

apply_async(func[, args[, kwds[, callback[, error_callback]]]]) : apply()方法的一个变体,会返回一个结果对象。如果callback被指定,那么callback可以接收一个参数然后被调用,当结果准备好回调时会调用callback,调用失败时,则用error_callback替换callback。 Callbacks应被立即完成,否则处理结果的线程会被阻塞。

close() : 阻止更多的任务提交到pool,待任务完成后,工作进程会退出。

terminate() : 不管任务是否完成,立即停止工作进程。在对pool对象进程垃圾回收的时候,会立即调用terminate()。

join() : wait工作线程的退出,在调用join()前,必须调用close() or terminate()。这样是因为被终止的进程需要被父进程调用wait(join等价与wait),否则进程会成为僵尸进程。

map(func, iterable[, chunksize])¶

map_async(func, iterable[, chunksize[, callback[, error_callback]]])¶

imap(func, iterable[, chunksize])¶

imap_unordered(func, iterable[, chunksize])

starmap(func, iterable[, chunksize])¶

starmap_async(func, iterable[, chunksize[, callback[, error_back]]])

协程

协程又叫微线程,从技术的角度来说,“协程就是你可以暂停执行的函数”。如果你把它理解成“就像生成器一样”,那么你就想对了。 线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

Event Loop

Event Loop是一种等待程序分配时间或消息的编程架构。简单的说就是 当事件A发生的时候,我们就去执行事件B。 最简单的例子就是:当我们浏览网页的时候,我们点击页面的某个元素,这个点击事件会被 JavaScript 捕捉到,然后 JavaScript 就会检查这个事件是否绑定了onclick()回调函数来处理这个事件,只要绑定了,onclick()回调函数就会被执行。

event loop是协程执行的控制点, 如果你希望执行协程, 就需要用到它们。

event loop提供了如下的特性:

注册、执行、取消延时调用(异步函数)

创建用于通信的client和server协议(工具)

创建和别的程序通信的子进程和协议(工具)

把函数调用送入线程池中

协程示例:

import asyncio

async def cor1():
print("COR1 start")
await cor2()
print("COR1 end")

async def cor2():
print("COR2")

loop = asyncio.get_event_loop()
loop.run_until_complete(cor1())
loop.close()


最后三行是重点:

asyncio.get_event_loop() : asyncio启动默认的event loop

run_until_complete() : 这个函数是阻塞执行的,知道所有的异步函数执行完成,

close() : 关闭event loop。

subprocess模块

通过使用subprocess模块可以创建新的进程,连接到他们的输入/输出/错误管道,并获取他们的返回值。 该模块计划替代及一个旧的模块的方法:

os.system
os.spawn*


使用subprocess模块

在所有用例调用subprocess时推荐使用run()方法,更高级的用例,可以直接使用subprocess.Popen接口。

run()方法

在Python3.5增加的。

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False)


run()默认不会捕捉到标准输出和标准错误输出,要捕捉的话,可以为标准输出和标准错误输出指定subprocess.PIPE(一个特殊值,可被用于Popen的stdin, stdout或 stderr参数,表示一个标准流的管道应该被打开, Popen.communicate()用的最多)。

args :args应该是一个字符串或者一个序列。

timeout:设置超时时间,会传递给subprocess.Popen.communicate()。如果超时,子进程会被杀死并等待。子进程被终止后会报告一个 TimeoutExpired异常。

input参数会传递给subprocess.Popen.communicate(),从而作为subprocess的标准输入。当我们使用的时候,内部的Popen对象会自动创建stdin=PIPE,stdin参数可能不会被使用。

check:如果check参数为True,且进程退出的时候得到退出码一个非0的值,会报告一个 CalledProcessError异常

shell:shell参数默认为False,此时arg参数应该是一个列表。
subprocess.run(["ls","-l"]) ;
当shell=True时,args可以是一个字符串。
subprocess.run("ls -l",shell=True)


>>> ret = subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
>>> print(ret.stdout)
None

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

>>> ret1 = subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

>>> print(ret.stdout)
b'crw-rw-rw- 1 root root 1, 3 6\xe6\x9c\x88   8 06:50 /dev/null\n'


call方法

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)¶
call()方法等价于:
run(..., check=True)
和run()方法类所以,只是不支持input参数和check参数;

注意: 不要在这个方法里使用stdout=PIPE 或 stderr=PIPE,当到一个管道的输出填满系统的管道缓存时,子进程会被阻塞。

check_call方法

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)


check_call()方法等价于:
run(..., check=True, stdout=PIPE).stdout


3.1新增,3.3时增加了timeout参数,3.4时增加了对关键字参数的支持

check_output()方法

内部调用的是run()方法,但是会捕捉到stdout

>>> ret = subprocess.check_output(["ls", "-l", "/dev/null"])
>>> print(ret)
b'crw-rw-rw- 1 root root 1, 3 6\xe6\x9c\x88   8 06:50 /dev/null\n'


Popen类

上面的四个方法本质上调用的都是subprocess中的Popen类。

Popen对象都有以下方法:

poll() : 检查子进程是否已经终止,返回一个returncode,相当于exit code。

wait() : 等待子进程终止,返回一个returncode

communicate(input=None) :和进程进行交互:发送数据到stdin;从stdout和stderr读取数据,直到读取完。等待进程终止。可选参数input会传递数据给子进程,当input=None事,没有数据传递给子进程。 communicate() 返回一个元组 (stdout, stderr). 注意: 读取的数据在内存的buffer中,所以当数据大小大于buffer或者没有限制时,不要使用这个方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: