进程是执行程序过程中产生一系列内容,是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位,每个进程中至少包括一个主线程,一个线程只能属于一个进程,而一个进程可以有多个线程,进程之间不能直接共享资源
python中常用的线程模块有_thread
,threading
打印激活的线程个数及线程信息
import threading
import time
def job():
print("这是一个需要执行的任务")
# 激活的线程个数
print("当前线程的个数:", threading.active_count())
# 打印当前线程的详细信息
print("当前线程信息:", threading.current_thread())
time.sleep(100)
job()
import _thread
import threading
import time
def job(name):
print("这是一个需要执行的任务")
# 激活的线程个数
print("当前线程的个数:", threading.active_count())
# 打印当前线程的详细信息
print("当前线程信息:", threading.current_thread())
print(name, time.ctime())
time.sleep(2)
if __name__ == "__main__":
# 创建多个线程, 但是没有开始执行任务;
_thread.start_new_thread(job, ('thread1',))
_thread.start_new_thread(job, ('thread2',))
while True:
pass
import threading
import time
def job(name):
print("这是一个需要执行的任务: %s" % (name))
# 激活的线程个数
print("当前线程的个数:", threading.active_count())
# 打印当前线程的详细信息
print("当前线程信息:", threading.current_thread())
time.sleep(1)
print(name, time.ctime())
if __name__ == "__main__":
# 创建多个线程
t1 = threading.Thread(target=job, name='job1', args=("job1-name",))
t1.start()
t2 = threading.Thread(target=job, name='job2', args=("job2-name",))
t2.start()
t1.join()
t2.join()
print('hello')
可以看到实现了多线程,线程个数统计包括主线程,t1.start()
表示启动线程,t1.join()
表示阻塞主线程,等待子进程 t1 执行结束后,执行主线程的内容
set_daemon
设置守护进程,当主线程执行结束,让没有执行的线程强制结束import threading
import time
# 任务1:
def music(name):
for i in range(2):
print("正在听音乐%s" % (name))
time.sleep(1)
print('完成')
# 任务2:
def code(name):
for i in range(2):
print("正在编写代码%s" % (name))
time.sleep(2)
print('完成')
if __name__ == '__main__':
start_time = time.time()
# music("中国梦")
# code("爬虫")
t1 = threading.Thread(target=music, args=("中国梦",))
t2 = threading.Thread(target=code, args=("爬虫",))
# 将t1线程生命为守护线程, 如果设置为True, 子线程启动, 当主线程执行结束, 子线程也结束
# 设置setDaemon必须在启动线程之前进行设置;
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
# 等待所有的子线程执行结束之后, 继续执行主线程的内容;
# t1.join()
# t2.join()
print("花费时间: %s" % (time.time() - start_time))
import threading
# 类的继承
class IpThread(threading.Thread):
# 重写构造方法;
def __init__(self, jobname):
super(IpThread, self).__init__()
self.jobname = jobname
# 将多线程需要执行的任务重写到run方法中;
def run(self):
print("this is a job")
t1 = IpThread(jobname="new job")
t1.start()
import json
import threading
from urllib.error import HTTPError
from urllib.request import urlopen
import time
class IpThread(threading.Thread):
# 重写构造方法;如果执行的任务需要传递参数, 那将参数通过构造函数与self绑定;
def __init__(self, jobname, ip):
super(IpThread, self).__init__()
self.jobname = jobname
self.ip = ip
# 将多线程需要执行的任务重写到run方法中;
def run(self):
try:
# 需要有一个参数, 传ip;
url = "http://ip.taobao.com/service/getIpInfo.php?ip=%s" % (self.ip)
# 根据url获取网页的内容, 并且解码为utf-8格式, 识别中文;
text = urlopen(url).read().decode('utf-8')
except HTTPError as e:
print("Error: %s获取地理位置网络错误" % (self.ip))
else:
# 将获取的字符串类型转换为字典, 方便处理
d = json.loads(text)['data']
country = d['country']
city = d['city']
print("%s:" % (self.ip), country, city)
def use_thread():
start_time = time.time()
ips = ['172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8',
'172.25.254.250', '8.8.8.8']
threads = []
for ip in ips:
t = IpThread(jobname="爬虫", ip=ip)
threads.append(t)
t.start()
# 等待所有的子线程执行结束
[thread.join() for thread in threads]
print("Success, 运行时间为%s" % (time.time() - start_time))
if __name__ == "__main__":
use_thread()
通过遍历参数列表,创建多线程并启动,在遍历完成后设置阻塞主线程,等待子进程全部完成后继续执行主线程
import threading
def add(lock):
# 2. 操作变量之前进行加锁
lock.acquire()
global money
for i in range(1000000):
money += 1
# 3. 操作变量完成后进行解锁
lock.release()
def reduce(lock):
# 2. 操作变量之前进行加锁
lock.acquire()
global money
for i in range(1000000):
money -= 1
# 3. 操作变量完成后进行解锁
lock.release()
if __name__ == '__main__':
money = 0
# 1. 实例化一个锁对象
lock = threading.Lock()
t1 = threading.Thread(target=add, args=(lock,))
t2 = threading.Thread(target=reduce, args=(lock,))
t1.start()
t2.start()
# 等待所有子线程执行结束
t1.join()
t2.join()
print("最终金额为:%s" % (money))
Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,使用threading包并不是一个很好的方法。
对于I/O密集型操作,适合使用多线程操作,对于CPU/计算密集型操作,则不适合使用多线程操作
import threading
from collections import Iterable
from queue import Queue
def job(l, queue):
# 将任务的结果存储到队列中;
queue.put(sum(l))
def use_thread():
# 实例化一个队列, 用来存储每个线程执行的结果;
q = Queue()
# # 入队
# q.put(1)
li = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7, 8], [2, 3, 4, 5, 6]]
threads = []
for i in li:
t = threading.Thread(target=job, args=(i, q))
threads.append(t)
t.start()
# join方法等待所有子线程之心结束
[thread.join() for thread in threads]
# 从队列里面拿出所有的运行结果
result = [q.get() for _ in li]
print(result)
print(isinstance(q, Iterable))
if __name__ == "__main__":
use_thread()
from concurrent.futures import ThreadPoolExecutor
import time
# 需要执行的任务
def job():
print("this is a job")
return "hello"
if __name__ == '__main__':
# 实例化对象, 线程池包含10个线程来处理任务;
pool = ThreadPoolExecutor(max_workers=10)
# 往线程池里面扔需要执行的任务, 返回一个对象,( _base.Future实例化出来的)
f1 = pool.submit(job)
f2 = pool.submit(job)
# 判断任务是否执行结束
print(f1.done())
time.sleep(1)
print(f2.done())
# 获取任务执行的结果
print(f1.result())
print(f2.result())
from urllib.error import HTTPError
from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import as_completed
import time
URLS = ['http://httpbin.org', 'http://example.com/',
'https://api.github.com/'] * 10
def get_page(url, timeout=3):
try:
content = urlopen(url).read()
return {'url': url, 'len': len(content)}
except HTTPError as e:
return {'url': url, 'len': 0}
# 方法1: submit提交任务
start_time = time.time()
pool = ThreadPoolExecutor(max_workers=20)
futuresObj = [pool.submit(get_page, url) for url in URLS]
# 注意: 传递的时包含futures对象的序列, as_complete, 返回已经执行完任务的future对象,
# 直到所有的future对应的任务执行完成, 循环结束;
for finish_fs in as_completed(futuresObj):
print(finish_fs.result() )
for future in futuresObj:
print(future.result())
print("执行时间:%s" % (time.time() - start_time))
#####
{'url': 'http://example.com/', 'len': 1270}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
执行时间:3.0462844371795654
方法二:
from urllib.error import HTTPError
from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor
URLS = ['http://httpbin.org', 'http://example.com/',
'https://api.github.com/'] * 10
def get_page(url, timeout=3):
try:
content = urlopen(url).read()
return {'url': url, 'len': len(content)}
except HTTPError as e:
return {'url': url, 'len': 0}
# 方法2:通过map方式执行
pool = ThreadPoolExecutor(max_workers=20)
for res in pool.map(get_page, URLS):
print(res)
#####
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
{'url': 'http://httpbin.org', 'len': 10122}
{'url': 'http://example.com/', 'len': 1270}
{'url': 'https://api.github.com/', 'len': 2039}
import os
import time
print("当前进程(pid=%d)正在运行..." % (os.getpid()))
print("当前进程的父进程(pid=%d)正在运行..." % (os.getppid()))
print("正在创建子进程......")
pid = os.fork()
pid2 = os.fork()
print("第1个:", pid)
print("第2个: ", pid2)
if pid == 0:
print("这是创建的子进程, 子进程的id为%s, 父进程的id为%s"
% (os.getpid(), os.getppid()))
else:
print("当前是父进程[%s]的返回值%s" % (os.getpid(), pid))
time.sleep(100)
#####
当前进程(pid=5714)正在运行...
当前进程的父进程(pid=5378)正在运行...
正在创建子进程......
第1个: 5715
第2个: 5716
当前是父进程[5714]的返回值5715
第1个: 5715
第2个: 0
当前是父进程[5716]的返回值5715
第1个: 0
第2个: 5717
这是创建的子进程, 子进程的id为5715, 父进程的id为5714
第1个: 0
第2个: 0
这是创建的子进程, 子进程的id为5717, 父进程的id为5715
Linux创建子进程的原理:先有父进程,再有子进程,通过 fork() 函数实现,如果父进程结束, 子进程也随之结束,其中fork函数的返回值:调用该方法一次, 返回两次,子进程返回一个0,父进程返回子进程的pid,os.getpid()
获取当前进程的pid,os.getppid()
获取当前进程的父进程的id
import multiprocessing
def job():
print("当前子进程的名称为%s" % (multiprocessing.current_process()))
# 创建一个进程对象
p1 = multiprocessing.Process(target=job)
# 运行多进程, 执行任务
p1.start()
# 创建一个进程对象
p2 = multiprocessing.Process(target=job)
# 运行多进程, 执行任务
p2.start()
# 等待所有的子进程执行结束, 再执行主进程的内容
p1.join()
p2.join()
print("任务执行结束.......")
#####
当前子进程的名称为
当前子进程的名称为
任务执行结束.......
import multiprocessing
class JobProcess(multiprocessing.Process):
# 重写Process的构造方法, 获取新的属性
def __init__(self, queue):
super(JobProcess, self).__init__()
self.queue = queue
# 重写run方法, 将执行的任务放在里面即可
def run(self):
print("当前子进程的名称为%s" % (multiprocessing.current_process()))
processes = []
# 启动10个子进程, 来处理需要执行的任务;
for i in range(10):
# 实例化对象;
p = JobProcess(queue=3)
processes.append(p)
# 启动多进程, 处理需要执行的任务;
p.start()
# 等待所有的子进程执行结束, 再继续执行主进程
[process.join() for process in processes]
# 执行主进程
print("任务执行结束.......")
#####
当前子进程的名称为
当前子进程的名称为
当前子进程的名称为
当前子进程的名称为
当前子进程的名称为
当前子进程的名称为
当前子进程的名称为
当前子进程的名称为
当前子进程的名称为
当前子进程的名称为
任务执行结束.......
对于多进程,开启的进程数有瓶颈,取决于CPU的个数。如果处理的数据比较小, 不建议使用多进程, 因为进程的创建和销毁需要时间,开启的进程数越多,不一定效率越高。如果处理的数据量足够大, 0<开启的进程数 在执行 被管理端: map方法
import multiprocessing
import time
def job():
name = multiprocessing.current_process()
print("开始运行")
time.sleep(3)
print("结束进程")
if __name__ == '__main__':
# 启动一个子进程
p1 = multiprocessing.Process(target=job, name='use deamon')
# True/False
p1.daemon = True
p1.start()
# join等待所有子进程执行结束, 再执行主进程
# p1.join()
# 主进程执行
print("程序执行结束")
import time
import multiprocessing
def job():
print("start.....")
time.sleep(10)
print('end.......')
if __name__ == '__main__':
p = multiprocessing.Process(target=job)
print("Before:", p.is_alive())
p.start() # 启动子进程
print("During:", p.is_alive())
p.terminate() # 终止子进程
print('terminate:', p.is_alive())
# p.join() # 等待子进程彻底终止
time.sleep(1)
print("joined:", p.is_alive())
#####
Before: False
During: True
terminate: True
joined: False
terminate()
后,进程被终止
import multiprocessing
from multiprocessing import Queue
import time
class Producer(multiprocessing.Process):
# 往队列里面写内容
def __init__(self, queue):
super(Producer, self).__init__()
self.queue = queue
def run(self):
for i in range(10):
self.queue.put(i)
time.sleep(0.1)
print("传递消息, 内容为:%s" % (i))
class Consumer(multiprocessing.Process):
# 读取队列里面的内容
def __init__(self, queue):
super(Consumer, self).__init__()
self.queue = queue
def run(self):
# 判断队列是否为空, 如果是, 跳出循环, 不会再去从队列获取数据;
# while not self.queue.empty():
while True:
time.sleep(0.1)
print("读取进程传递的消息:%s" % (self.queue.get()))
if __name__ == "__main__":
q = Queue()
p1 = Producer(q)
c1 = Consumer(q)
p1.start()
c1.start()
p1.join()
c1.terminate()
c1.join()
print('all done')
#
读取进程传递的消息:0
传递消息, 内容为:0
读取进程传递的消息:1
传递消息, 内容为:1
读取进程传递的消息:2
传递消息, 内容为:2
读取进程传递的消息:3
传递消息, 内容为:3
读取进程传递的消息:4
传递消息, 内容为:4
读取进程传递的消息:5
传递消息, 内容为:5
读取进程传递的消息:6
传递消息, 内容为:6
读取进程传递的消息:7
传递消息, 内容为:7
读取进程传递的消息:8
传递消息, 内容为:8
读取进程传递的消息:9
传递消息, 内容为:9
all done
当任务需要处理的数据非常大,希望多台主机共同处理任务
实现方式:
multiprocessing.managers子模块里面可以实现将进程分布到多台机器
BaseManager: 提供了不同机器进程之间共享数据的一种方法(ip:port)
Master: 管理端, 分配任务给其他主机;
Worker1: 被管理端, 处理master给予的任务;
Worker2:被管理端, 处理master给予的任务;
管理端:import random
from queue import Queue
from multiprocessing.managers import BaseManager
# 1. 创建需要的队列
# task_queue存储的是任务需要传递的参数
task_queue = Queue()
# result_queue存储的是任务执行结果
result_queue = Queue()
# 2. 将队列注册到网络上
# 需要将两个队列注册到网络上, 使得其他主机可以访问;
BaseManager.register('get_task_queue', callable=lambda : task_queue)
BaseManager.register('get_result_queue', callable=lambda : result_queue)
# 绑定端口为为4000, 暗号/密钥为hello;
manager = BaseManager(address=('192.168.1.137', 4000), authkey=b'hello')
# 3. 启动manager, 开始共享队列;
manager.start()
# 4. 通过网络访问共享的Queue对象,
# manager.register: 注册一个队列, 唯一标识'get_task_queue'
# manager.get_task_queue()调用注册, 调用过程中执行的内容为callable只想的函数;
task = manager.get_task_queue()
result = manager.get_result_queue()
# 5. 开始往队列里面放执行任务需要的数据;
for i in range(100):
n = random.randint(1,1000)
task.put(n)
print("任务列表加入任务: %d" %(n))
# 6. 从result队列里面读取各个及机器执行的结果;
for j in range(100):
res = result.get()
print("队列任务的执行结果:%s" %(res))
# 7. 关闭manager, 取消共享队列;
manager.shutdown()
from multiprocessing.managers import BaseManager
# 1. 连接Master端, 获取共享的队列;
# address写的是master端的ip和共享的端口, authkey与master端保持一致;
import time
worker = BaseManager(address=('192.168.1.137', 4000), authkey=b'hello')
# 2. 注册队列, 获取共享端的队列内容
BaseManager.register('get_task_queue')
BaseManager.register('get_result_queue')
# 3. 去连接
worker.connect()
# 4. 通过网络访问共享的Queue对象,
# manager.register: 注册一个队列, 唯一标识'get_task_queue'
# manager.get_task_queue()调用注册, 调用过程中执行的内容为callable只想的函数;
task = worker.get_task_queue()
result = worker.get_result_queue()
# 5. 读取管理端共享的任务, 依次执行;
for i in range(50):
n = task.get()
print("运行任务 %d ** 2:" %(n))
res = "%d ** 2 = %d" %(n, n**2)
time.sleep(1)
# 将运行结果放入reesult队列
result.put(res)
print("执行结束.....")
import multiprocessing
def job(id):
print('start %d.....' % (id))
print('end %d.....' % (id))
# 创建一个进程池对象
pool = multiprocessing.Pool(10)
# 给进程池的进程分配任务
for i in range(10):
pool.apply_async(job, args=(i,))
pool.close()
# 等待所有的子进程执行结束
pool.join()
print('success')
#
start 0.....
end 0.....
start 1.....
end 1.....
start 3.....
start 2.....
end 3.....
end 2.....
start 5.....
end 5.....
start 4.....
end 4.....
start 6.....
end 6.....
start 7.....
end 7.....
start 8.....
end 8.....
start 9.....
end 9.....
success
submit方法from concurrent.futures import ProcessPoolExecutor
def job(id):
print('start %d.....' % (id))
print('end %d.....' % (id))
return id
# submit
pool = ProcessPoolExecutor(max_workers=4)
# 分配任务给子进程, 并且返回一个Future对象;
f1 = pool.submit(job, 1)
# 获取进程是否执行结束;
f1.done()
# 获取子进程执行的结果
f1.result()
#
start 1.....
end 1.....
from concurrent.futures import ProcessPoolExecutor
def job(id):
print('start %d.....' % (id))
print('end %d.....' % (id))
return id
pool = ProcessPoolExecutor(max_workers=4)
for res in pool.map(job, range(1, 100)):
print(res)