网络编程五:多线程
一、并发
并发:假同时。一段时间内,同时处理多个任务;如果是单CPU处理,则是单CPU轮询,轮流处理多个任务;如果是多个CPU同时处理,每个CPU轮询上轮流处理多个任务,CPU与CPU之间并行处理多个任务。
并行:真同时。同时处理多个任务,必须要多核,多个CPU同时处理多个任务,一个CPU处理一个任务。
主流程操作系统:提供线程、进程。
主流语言提供的用户空间调度:协程。
主流语言提供的操作系统调度:线程、进程。
一个进程,可有启用多个线程;一个进程,可以启用多个子进程。
在python中,一个进程启用一个python解释器;一个进程中的多个线程,共享一个进程资源,共享一个python解释器。
查看python进程
$ ps -ef | grep python
查看某个python进程中的线程
$ ps -efL | grep 进程号
-L参数,用于显示线程。
二、线程方式一:threading.Thread
import threading def work(num): print ("work-{}".format(num)) thread = threading.Thread(target=work, args=(1, )) #创建线程对象,target参数是一个函数,这个函数即线程执行的逻辑 thread.start() # start方法启动一个线程;当这个线程的逻辑执行完的时间,线程自动退出 # python中,没有手动退出线程的方法。线程执行完函数,自动退出;或者主线程退出,子线程退出。
如何标识一个线程?
threading.current_thread() 返回当前线程的对象
name:此对象的name属性,为此线程的名称,子线程默认为Thread-1、Thread-2....,主线程默认为MainThread
is_alive():线程对象is_alive()方法,判断此线程是否存活。
obj = threading.current_thread() print(obj.name) # MainThread print(obj.is_alive()) # True
三、daemon线程、non-daemon线程
import logging,threading,time logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s [%(threadName)s] %(message)s') def worker(): logging.info("start") time.sleep(5) logging.info("completed") if __name__ == '__main__': logging.info("starting") t1 = threading.Thread(target=worker, name="non-daemon") t1.start() t2 = threading.Thread(target=worker, name="non-daemon", daemon=False) t2.start()
time.sleep(1) t3 = threading.Thread(target=worker, name="daemon", daemon=True) t3.start() logging.info("completed")
线程默认为non-daemon线程,即daemon默认为False。
daemon线程,会跟随主线程退出而退出;non-daemon线程,不会跟随主线程退出而退出。
主线程不会等待daemon线程退出;会等待non-daemon线程退出。
2018-11-03 15:23:03,972 INFO [MainThread] starting 2018-11-03 15:23:03,972 INFO [non-daemon] start 2018-11-03 15:23:03,972 INFO [non-daemon] start 2018-11-03 15:23:03,972 INFO [daemon] start 2018-11-03 15:23:03,972 INFO [MainThread] completed ****************** 2018-11-03 15:23:08,972 INFO [non-daemon] completed 2018-11-03 15:23:08,972 INFO [non-daemon] completed
在此示例中,主线程退出的时侯,daemon线程还在执行time.sleep(5),此时会强制daemon线程退出。
此处的daemon线程,和linux系统的守护进程,不是一个概念。
三、线程的join()方法
线程的join方法会阻塞线程,直到线程退出。
因此,使用join方法的daemon线程,主线程也会等待它退出。
示例:
import threading, time def worker(num): time.sleep(2) print(num) for i in range(5): t = threading.Thread(target=worker, args=(i,)) t.start() t.join()
以上示例中,所有的子线程都调用join方法,那么所有线程都将依次执行。
即执行完线程1,才会执行线程2.........依次执行
谁调用join,谁等待,阻塞谁。
import threading, time def worker(num): time.sleep(5) print(num) t1 = threading.Thread(target=worker, args=(1,)) t1.start() t1.join() t2 = threading.Thread(target=worker, args=(1,)) t2.start() t3 = threading.Thread(target=worker, args=(1,)) t3.start()
t1调用了join方法,那么t1将等待t1执行完成后,才会执行后面的代码,将串行执行。
t2没有调用join方法,那么t2及后面的代码t3线程,将并发执行。
1 # 执行线程1,由于调用了join方法,将阻塞,将待待t1执行完毕,才会执行后面的代码。这里花费5秒。 1 # 执行线程2,由于没有调用join方法,将不会阻塞线程2,即后面的代码t3线程将并发执行。 1 # 线程2和线程3,都将在5后执行完毕。
joint()方法的参数timeout:在超出此时间时,仍未执行完此线程,则将不会继续阻塞后面的代码。
四、启动多线程方式二:继承threading.Thread
不建议使用此方式
import threading class MyThread(threading.Thread): def run(self): print ("run......") t = MyThread() t.start() t.run()
非继承方法,实现的线程,run和start方法只能执行其中一个。
继承方法,实现的线程,run和start方法,可以各执行一次。
五、启动线程方式三:线程池ThreadPoolExecutor
def worker(num): print(num) from concurrent.futures import ThreadPoolExecutor executor = ThreadPoolExecutor(max_workers=10) for i in range(10,13): executor.submit(worker, i) executor.map(worker, [1,2,3])
10 11 12 1 2 3
六、线程的私有属性:
local = threading.local()
local对所有线程可见,但是local的属性在哪个线程定义,则在哪个线程可见,其它线程不可见。
import threading data = "abc" local = threading.local() local.data = "123" def worker(): print(data) print(local.data) if __name__ == '__main__': worker() threading.Thread(target=worker).start()
abc #全局变量data,所有线程共享 123 # local.data,在主线程中定义,在主线程中可见,在其它线程中不可见 abc #全局变量data,所有线程共享 #在子线程中,不可见主线程的私有变量local.data,因此将抛出错误:local没有data属性 #AttributeError: '_thread._local' object has no attribute 'data'
七、定时器、延迟执行:threading.Timer
import threading def worker(num): print(num) t = threading.Timer(interval=5, function=worker, args=(1,)) t.start()
示例中,线程的worker函数将延迟5秒执行。在worker实际执行之前,可以取消worker的执行。
posted on 2018-11-03 15:35 myworldworld 阅读(108) 评论(0) 收藏 举报