1.  
  2. 主页
  3.  / 
  4. Python基础到高级
  5.  / 
  6. 并发

并发

并发

说到并发的时候,你应该也同时要想到并行,那么并行和并发的区别是啥呢?

并发是一种假的同一时间段处理多个任务的操作,服务器是单核的话,也是可以实现并发的

并行是真的同一时间处理多个任务,但是服务器的配置必须是多核的

在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是无效的

这篇文章对您有用吗? 1

我们要如何帮助您?

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注