目录
3)Process([group [, target [, name [, args [, kwargs]]]]])
1.apply_async(func[, args[, kwds[, callback[, error_callback]]]])
🚩进程和线程的概念
进程是资源分配的基本单位,它是程序执行时的一个实例
在程序运行时创建;线程是程序执行的最小单位,是进程的一个执行流
简单来说线程是进程中的一部分,进程包含多个线程在运行
🏴☠️补充:
一个线程由多个协程组成的
🏴☠️区别:
线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。
🚩多进程
python内置os模块,让Python程序中轻松创建子进程
🏴☠️multiprocessing
由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。
multiprocessing
模块就是跨平台版本的多进程模块,不管window还是mac,unix都可用
例如:启动一个子进程并等待其结束:
from multiprocessing import Process
import os
# 子进程要执行的代码
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
output:
🏴☠️代码解读
1)join()方法
当一个线程操作需要等待另一个线程执行完毕之后才能继续进行时,使用Join()方法。Join方法会等到使用该方法的线程结束后再执行下面的代码
2)start()方法
开始线程
3)Process([group [, target [, name [, args [, kwargs]]]]])
注意:1. 必须使用关键字方式来指定参数;
2. args指定的为传给target函数的位置参数,是一个元祖形式,必须有逗号
1.group应该是None
;为将来ThreadGroup
实现类时的扩展保留 。
2.target是run()方法要调用的可调用对象。默认为None
,意味着什么都不调用。
3.name是线程名称。默认情况下,唯一名称的构造形式为“Thread- N ”,其中N是小十进制数,或者“Thread- N (target)”,其中“target”是指定target.__name__
了 目标参数的情况。
4.args是目标调用的参数元组。默认为()
.
5.kwargs是目标调用的关键字参数字典。默认为{}
.
6.如果不是None
,守护进程显式设置线程是否是守护进程。如果None
(默认),守护进程属性是从当前线程继承的。
4)getpid
返回进程ID。在进程产生之前,这将是
None
.
🚩Pool进程池
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
🏴☠️代码解读:
1.apply_async(func[, args[, kwds[, callback[, error_callback]]]])
apply() 方法的一个变种,返回一个 AsyncResult 对象。
如果指定了 callback , 它必须是一个接受单个参数的可调用对象。
当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。
如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。
回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。
2)注意调用
join()
之前必须先调用close()
,调用close()
之后就不能继续添加新的Process
了。
🚩子进程
subprocess
模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出
"""
CSDN : heart_6662
PYTHON amateur
"""
import subprocess
print('$ nslookup www.python.org')
r = subprocess.call(['nslookup', 'www.python.org'])
print('Exit code:', r)
output:
🚩进程间通信
Process
之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing
模块包装了底层的机制,提供了Queue
、Pipes
等多种方式来交换数据。以
Queue
为例,在父进程中创建两个子进程,一个往Queue
里写数据,一个从Queue
里读数据:
from multiprocessing import Process, Queue
import os, time, random
# 写数据进程执行的代码:
def write(q):
print('Process to write: %s' % os.getpid())
for value in ['A', 'B', 'C']:
print('Put %s to queue...' % value)
q.put(value)
time.sleep(random.random())
# 读数据进程执行的代码:
def read(q):
print('Process to read: %s' % os.getpid())
while True:
value = q.get(True)
print('Get %s from queue.' % value)
if __name__=='__main__':
# 父进程创建Queue,并传给各个子进程:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,读取:
pr.start()
# 等待pw结束:
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止:
pr.terminate()
🚩多线程
多任务可以由多进程完成,也可以由一个进程内的多线程完成
很多高级语言都内置线程,而Python的线程是真正的Posix Thread,而不是模拟出来的线程
🏴☠️分类:
_thread
和threading
,_thread
是低级模块,threading
是高级模块,对_thread
进行了封装。绝大多数情况下,我们只需要使用threading
这个高级模块。
例如:
启动一个线程就是把一个函数传入并创建Thread
实例,然后调用start()
开始执行
import time, threading
# 新线程执行的代码:
def loop():
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n = n + 1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)
🏴☠️线程与进程的区别
就是内容共享问题
多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,
而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了
例如:
import time, threading
# 假定这是你的银行存款:
balance = 0
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(2000000):
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
代码说明:
定义了一个共享变量
balance
,初始值为0
,并且启动两个线程,先存后取,理论上结果应该为0
可结果为-3
原因:
因为高级语言的一条语句在CPU执行时是若干条语句,即使一个简单的计算;例如
balance = balance + n
也分两步:
- 计算
balance + n
,存入临时变量中;- 将临时变量的值赋给
balance
。x = balance + n balance = x
问题解决:
核心在于限制
如果我们要确保balance
计算正确,就要给change_it()
上一把锁,当某个线程开始执行change_it()
时,该线程因为获得了锁,因此其他线程不能同时执行change_it()
,直到锁被释放后,获得该锁以后才能改。
由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。
创建一个锁就是通过threading.Lock()
来实现:将代码加上我们的锁:
"""
CSDN : heart_6662
PYTHON amateur
"""
import time, threading
# 假定这是你的银行存款:
balance = 0
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
不管怎么运行,你的结果这次一次为0