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:使用函数
importrandomimportthreadingimporttimedefrun(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类似)
importrandomimportthreadingimporttimeclasstest(threading.Thread):def __init__(self,name):
super().__init__()
self.name=namedefrun(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方法
importrandomimportthreadingimporttimedefrun():
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 inthreads:
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方法
importrandomimportthreadingimporttimedefrun():
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 inthreads:
th.start()#等待,直到线程终结。
for th inthreads:
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如果还没执行完则会被终止。
importthreadingimporttimedefrun():
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 importThread, Lock
lock=Lock()
total=0#如果不使用lock那么,最后得到的数字不一定为0;
defcal(type):globaltotalfor i in range(1000000):
lock.acquire()if(type == '+'):
total+= 1
else:
total-= 1lock.release()
thread1= Thread(target=cal,args=('+',))
thread2= Thread(target=cal,args=('-',))
thread1.start()
thread2.start()
thread1.join()
thread2.join()print(total)