并发
说到并发的时候,你应该也同时要想到并行,那么并行和并发的区别是啥呢?
并发是一种假的同一时间段处理多个任务的操作,服务器是单核的话,也是可以实现并发的
并行是真的同一时间处理多个任务,但是服务器的配置必须是多核的
在Python中实现并发的手段:
在目前主流的操作系统中,实现并发的手段是 "线程" 与 "进程"
在主流的语言中通常提供用户空间的调度: "协程"
线程
线程和进程的区别:
一个进程里面可以存在多个线程,在linux中线程是通过进程实现的
而在Python中每个进程都会启动一个解释器,但是线程是共享一个解释器的
Python中是通过第三方库(treading)来实现多线程的
那我们来创建并执行一下线程对象:
In [1]: import threading
In [2]: def worker():
...: print('worker')
...:
In [3]: thread = threading.Thread(target=worker) # 创建一个线程对象,target传入的是一个函数对象
In [4]: thread.start() # 启动线程,只有执行start()的时候,才会执行线程对象
worker
当线程执行完成后就会自动销毁线程,并且Python没有主动退出线程的方法,只有逻辑完成才会退出
标识一个线程
In [5]: threading.current_thread() # 返回当前线程
Out[5]: <_MainThread(MainThread, started 140235663935296)>
In [6]: threading.Thread(target=lambda: print(threading.current_thread())).start()
<Thread(Thread-651, started 140235409147648)>
In [7]: thread = threading.current_thread()
In [8]: thread.isAlive() # 判断线程是否存活
Out[8]: True
In [9]: thread.name # 线程的名字
Out[9]: 'MainThread'
In [10]: thread.ident # 线程的ID
Out[10]: 140235663935296
logging让线程的输出更规范
当多线程对象直接返回结果的时候,返回值可能不是很规范
当遇到这样的情况,通常会使用logging来代替打印的方法
In [14]: import logging
In [15]: logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(threadName)s %(message)s')
In [16]: def worker(num):
...: logging.warning('worker-{}'.format(num))
...:
In [17]: for x in range(5):
...: t = threading.Thread(target=worker, args=(x, ))
...: t.start()
...:
WARNING:root:worker-0
WARNING:root:worker-1
WARNING:root:worker-2
WARNING:root:worker-3
WARNING:root:worker-4
daemon 与 non-daemon
daemon 与 non-daemon,线程退出时,其daemon子线程也会退出,而non-daemon子线程不会退出
线程退出的时候,会等待所有的non-daemon退出
实例:
import time
import logging
import threading
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(threadName)s %(message)s')
def worker():
logging.info('starting')
time.sleep(2)
logging.info('completed')
if __name__ == '__main__':
t1 = threading.Thread(target=worker, name='non-daemon')
t1.start()
t2 = threading.Thread(target=worker, name='daemon', daemon=True)
t2.start()
logging.info('completed')
结果:
2017-09-12 19:18:43,907 INFO non-daemon starting
2017-09-12 19:18:43,907 INFO daemon starting
2017-09-12 19:18:43,907 INFO MainThread completed
2017-09-12 19:18:45,907 INFO non-daemon completed
获取所有线程
In [1]: import threading
In [2]: threading.enumerate()
Out[2]:
[<_MainThread(MainThread, started 139669826524992)>,
<HistorySavingThread(IPythonHistorySavingThread, started 139669593855744)>]
In [3]: for t in threading.enumerate():
...: print(t.name)
...:
MainThread
IPythonHistorySavingThread
使用继承的方式创建线程
In [13]: class MyThread(threading.Thread):
...: def run(self):
...: logging.warning('lanyulei')
...:
...:
In [14]: t = MyThread()
In [15]: t.run()
WARNING:root:lanyulei
In [16]: t.start()
WARNING:root:lanyulei
通常我们不建议使用继承的方式来创建线程
t.json()
json 方法会执行线程直到线程阻塞然后退出
当使用theading创建线程的时候,当调用了run方法后, 就无法调用start方法了
run方法是在父线程内执行,start时直接创建线程执行
thread local
thread local相当于一种变量属性,但是这种变量属性,仅在当前线程可见,是线程独享的资源
In [23]: cxs = threading.local()
In [24]: cxs.data = 5
In [25]: data = 3
In [28]: def worker():
...: logging.warning(data)
...: logging.warning(cxs.data)
...:
...:
In [29]: worker()
WARNING:root :3
WARNING:root:5
In [30]: threading.Thread(target=worker).start() # 当在其他线程使用thread local 成员属性的时候,是不行的
WARNING:root:3
Exception in thread Thread-2702:
Traceback (most recent call last):
File "/root/.pyenv/versions/3.6.1/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/root/.pyenv/versions/3.6.1/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "<ipython-input-28-ce3f84bb67d1>", line 3, in worker
logging.warning(cxs.data)
AttributeError: '_thread._local' object has no attribute 'data'
定时器/延迟执行
In [1]: import logging
In [2]: import threading
In [6]: def worker():
...: logging.warning('run')
...:
In [7]: t = threading.Timer(interval=5, function=worker) # 定义五秒后执行函数 worker
In [8]: t.start()
In [9]: WARNING:root:run
In [10]: t.is_alive()
Out[10]: False
延迟执行时可以被取消的
t.cancel() # 这样就可以取消延迟执行了,结束线程
当function参数所指定函数开始执行的时候 ,cancel是无效的