网络编程五:多线程

一、并发

并发:假同时。一段时间内,同时处理多个任务;如果是单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)    收藏  举报

导航