Python2标准库中提供了两个模块thread和threading支持多线程。
thread有一些缺陷在Python3中弃用,为了兼容性,python3 将 thread 重命名为 "_thread",在Python3中推荐直接使用threading。
创建线程对象
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
参数说明:
group 应该为 None;为了日后扩展 ThreadGroup 类实现而保留。 target 是用于 run() 方法调用的可调用对象。默认是 None,表示不需要调用任何方法。 name 是线程名称。默认情况下,由 "Thread-N" 格式构成一个唯一的名称,其中 N 是小的十进制数。 args 是用于调用目标函数的参数元组。默认是 ()。 kwargs 是用于调用目标函数的关键字参数字典。默认是 {}。 如果 daemon 不是 None,线程将被显式的设置为 守护模式,不管该线程是否是守护模式。如果是 None (默认值),线程将继承当前线程的守护模式属性。
线程对象其中的几个方法
start()开始线程活动。 run() 代表线程活动的方法。 join(timeout=None) 等待,直到线程终结
setDaemon()设置是否为守护线程
一、使用线程有两种方式:函数或者用类来包装线程对象
例子1:使用函数
import random import threading import time def run(threadName): for i in range(5): time.sleep(random.random()) #这里只是演示参数,获取当前线程名可用threading.currentThread().getName() print('{} : {}'.format(threadName, i)) t1 = threading.Thread(target=run, args=('thread 1',)) t1.start() t2 = threading.Thread(target=run, args=('thread 2',)) t2.start() ''' 运行结果: thread 2 : 0 thread 2 : 1 thread 1 : 0 thread 1 : 1 thread 2 : 2 thread 2 : 3 thread 1 : 2 thread 1 : 3 thread 2 : 4 thread 1 : 4 '''
例子2:使用类(运行结果和例子1类似)
import random import threading import time class test(threading.Thread): def __init__(self,name): super().__init__() self.name = name def run(self): for i in range(5): time.sleep(random.random()) print('{} : {}'.format(self.name, i)) t1 = test('thread 1') t1.start() t2 = test('thread 2') t2.start()
二、join(timeout=None) 方法
一个进程启动之后,会默认产生一个主线程A,当创建子线程B后,如果不调用B.join(),则主线程A和子线程B会同时执行;
如果调用B.join()则主线程会在调用处等待子线程B执行完后,才继续往下执行。
参数timeout是可选的,代表线程运行的最大时间,如果超过这个时间,不管这个线程有没有执行完毕都会被回收,然后主线程会接着执行。
join在start后才调用。
1、不调用子线程join方法
import random import threading import time def run(): name = threading.currentThread().getName() print('{} start'.format(name)) time.sleep(random.random()) print('{} end'.format(name)) threads = [] for i in range(5): threads.append(threading.Thread(target=run)) for th in threads: th.start() print("finished") ''' 运行结果 Thread-1 start Thread-2 start Thread-3 start Thread-4 start Thread-5 startfinished Thread-5 end Thread-3 end Thread-2 end Thread-4 end Thread-1 end '''
2、调用子线程join方法
import random import threading import time def run(): name = threading.currentThread().getName() print('{} start'.format(name)) time.sleep(random.random()) print('{} end'.format(name)) threads = [] for i in range(5): threads.append(threading.Thread(target=run)) for th in threads: th.start() #等待,直到线程终结。 for th in threads: th.join() print("finished") ''' 运行结果 Thread-1 start Thread-2 start Thread-3 start Thread-4 start Thread-5 start Thread-3 end Thread-5 end Thread-4 end Thread-1 end Thread-2 end finished '''
三、setDaemon()方法
主线程A中创建子线程B后,如果调用B.setDaemon(),把子线程设置为守护线程。
表示主线程A一旦执行结束,不管子线程B是否执行完成,会全部被终止。
setDaemon和join相反,在start之前设置。
备注:
主线程在其它非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将会被回收,而进程必须保证非守护线程都运行完毕后才能结束。
所以如果存在多个子线程,假设有守护线程B,又有非守护线程C,则主线程会等待非守护线程C执行完,守护线程B如果还没执行完则会被终止。
import threading import time def run(): name = threading.currentThread().getName() print('{} start'.format(name)) time.sleep(2) #下面这行代码因为主线程已经执行结束,被终止执行 print('{} end'.format(name)) print("start") t = threading.Thread(target=run) t.setDaemon(True) t.start() time.sleep(1) print("finished") ''' 运行结果 start Thread-1 start finished '''
四、线程锁
每个线程互相独立,相互之间没有任何关系,但是在同一个进程中的资源,线程是共享的,如果不进行资源的合理分配,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。
下面例子,如果不用线程锁Lock,则每次得到的结果都不一样。
from threading import Thread, Lock lock = Lock() total = 0 #如果不使用lock那么,最后得到的数字不一定为0; def cal(type): global total for i in range(1000000): lock.acquire() if(type == '+'): total += 1 else: total -= 1 lock.release() thread1 = Thread(target=cal,args=('+',)) thread2 = Thread(target=cal,args=('-',)) thread1.start() thread2.start() thread1.join() thread2.join() print(total)