注:这篇文章部分为廖雪峰py3教程:点我,本文使用python3.5
线程的使用场景:
python有GIL锁(global Interpreter lock),由于这个锁的存在,在任意给定时刻只有一个线程在执行。这里有一篇讲得稍微详细的文章:
点击打开链接
对于任意面向I/O的python程序,GIL会在I/O调用前释放,以允许其他线程在I/O执行的时候运行。
所以来说,在I/O密集型的python程序要比计算密集型的程序能够更好的利用多线程
常用的methods
(1)thread模块不支持守护线程,守护线程表示这个线程不是重要的,进程退出时不需要等待这个线程执行完成。在python的另一个模块threading中支持守护线程。
(2)threading.current_thread().name:获取当前线程的名字
(3)线程实例.start():线程开始执行
(4)线程实例.join():等待目前正在执行的线程执行完毕
(5)线程实例.daemon = True:设置该线程为守护线程
例子一:
import time
import threading
def run_func():
print( '[+]thread %s is running...' % threading.current_thread().name)
cnt = 0
while cnt < 5:
cnt += 1
print( 'thread %s >>> %s' % (threading.current_thread().name , cnt))
time.sleep( 1)
print( 'thread %s ended' % threading.current_thread().name)
if __name__ == '__main__':
print( 'thread %s is running..' % threading.current_thread().name)
t = threading.Thread( target=run_func , name= 'LoopThread')
t.start()
t.join()
print( '[+]thread %s ended' % threading.current_thread().name)
import threading
def run_func():
print( '[+]thread %s is running...' % threading.current_thread().name)
cnt = 0
while cnt < 5:
cnt += 1
print( 'thread %s >>> %s' % (threading.current_thread().name , cnt))
time.sleep( 1)
print( 'thread %s ended' % threading.current_thread().name)
if __name__ == '__main__':
print( 'thread %s is running..' % threading.current_thread().name)
t = threading.Thread( target=run_func , name= 'LoopThread')
t.start()
t.join()
print( '[+]thread %s ended' % threading.current_thread().name)
例子二:
import threading
import time
def func(slp):
name = threading.current_thread().name
print( '[+]thread%s start at ' % name , time.ctime())
time.sleep(slp)
print( '[+]thread%s end at ' % name , time.ctime())
if __name__ == '__main__':
trds = list()
slps = [x* 2 for x in range( 1 , 5)]
for each in range( 4):
trds.append(threading.Thread( target=func , args=(slps[each] , )))
print( '[+]main time start at' , time.ctime())
for each in trds:
each.start()
for each in trds:
each.join()
print( '[+]main time end at ' , time.ctime())
'''
output:
[+]main time start at Sun Feb 5 17:02:13 2017
[+]threadThread-1 start at Sun Feb 5 17:02:13 2017
[+]threadThread-2 start at Sun Feb 5 17:02:13 2017
[+]threadThread-3 start at Sun Feb 5 17:02:13 2017
[+]threadThread-4 start at Sun Feb 5 17:02:13 2017
[+]threadThread-1 end at Sun Feb 5 17:02:15 2017
[+]threadThread-2 end at Sun Feb 5 17:02:17 2017
[+]threadThread-3 end at Sun Feb 5 17:02:19 2017
[+]threadThread-4 end at Sun Feb 5 17:02:21 2017
[+]main time end at Sun Feb 5 17:02:21 2017
'''
import time
def func(slp):
name = threading.current_thread().name
print( '[+]thread%s start at ' % name , time.ctime())
time.sleep(slp)
print( '[+]thread%s end at ' % name , time.ctime())
if __name__ == '__main__':
trds = list()
slps = [x* 2 for x in range( 1 , 5)]
for each in range( 4):
trds.append(threading.Thread( target=func , args=(slps[each] , )))
print( '[+]main time start at' , time.ctime())
for each in trds:
each.start()
for each in trds:
each.join()
print( '[+]main time end at ' , time.ctime())
'''
output:
[+]main time start at Sun Feb 5 17:02:13 2017
[+]threadThread-1 start at Sun Feb 5 17:02:13 2017
[+]threadThread-2 start at Sun Feb 5 17:02:13 2017
[+]threadThread-3 start at Sun Feb 5 17:02:13 2017
[+]threadThread-4 start at Sun Feb 5 17:02:13 2017
[+]threadThread-1 end at Sun Feb 5 17:02:15 2017
[+]threadThread-2 end at Sun Feb 5 17:02:17 2017
[+]threadThread-3 end at Sun Feb 5 17:02:19 2017
[+]threadThread-4 end at Sun Feb 5 17:02:21 2017
[+]main time end at Sun Feb 5 17:02:21 2017
'''
守护线程:
import threading import time def func(output, sleeptime): print('[+]start at', time.ctime()) print('%s' % threading.current_thread().name, output) time.sleep(sleeptime) print('[+]end at', time.ctime()) if __name__ == '__main__': sleeptime = 5 thr = threading.Thread(name='lrh', target=func, args=('test now...', sleeptime)) thr.daemon = True thr.start() ''' #设置为守护线程后,程序执行完thr.start()后就开始退出, #由于执行了sleep(),输出是不会同时有start at 时间,end at 时间的 #output: [+]start at Thu Feb 16 17:40:07 2017 '''
线程锁
进程之间的变量是拷贝的,而线程是共用变量,所有当多个线程存在时需要限制其他线程不能使用,仅某个线程可以使用,这就是线程锁,
import threading
import os
import time
import random
balance = 0
lock = threading.Lock()
def run_func(n):
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for each in range( 10000):
lock.acquire()
try:
run_func(n)
except Exception as error:
print(error)
finally:
lock.release()
t1 = threading.Thread( target=run_thread , args=( 5000099 ,))
t2 = threading.Thread( target=run_thread , args=( 100000 ,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
import os
import time
import random
balance = 0
lock = threading.Lock()
def run_func(n):
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for each in range( 10000):
lock.acquire()
try:
run_func(n)
except Exception as error:
print(error)
finally:
lock.release()
t1 = threading.Thread( target=run_thread , args=( 5000099 ,))
t2 = threading.Thread( target=run_thread , args=( 100000 ,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
多个线程执行lock.acquire()时,只有一个线程能成功的获取锁,其他线程只能等待,所以一定要记得释放锁
此外,在python中可以使用上下文管理,即使用with语句。
多核cpu:
import threading
import multiprocessing
def loop():
x = 0
while True:
x = x ^ 1
for i in range(multiprocessing.cpu_count()):
t = threading.Thread( target=loop)
t.start()
import multiprocessing
def loop():
x = 0
while True:
x = x ^ 1
for i in range(multiprocessing.cpu_count()):
t = threading.Thread( target=loop)
t.start()
由于GIL全局锁把所有的线程的执行上了锁,多线程在python中只能交替的运行,即使100个线程跑在100核cpu上,也只能用到一个核
ThreadLocal,解决多线程局部变量的传递问题
由于线程之间的全局变量是共用的,所以对于一个线程来说,使用局部变量是最好的,避免影响其他线程之间的变量,那么就会有一个问题,局部变量在传递起来就会比较麻烦
一个可行的思路是使用线程名字作为key,value来存放待传递变量
python给我们提供了更加方便的做法,使用ThreadLocal
import threading
localSet = threading.local()
def func_run():
std = localSet.student
print( 'Hello, %s (in %s)' % (std , threading.current_thread().name))
def thread_run(name):
localSet.student = name
func_run()
if __name__ == '__main__':
t1 = threading.Thread( target=thread_run , args=( 'lrh' , ) , name= 'Thread-A')
t2 = threading.Thread( target=thread_run , args=( 'kch' ,) , name= 'Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
localSet = threading.local()
def func_run():
std = localSet.student
print( 'Hello, %s (in %s)' % (std , threading.current_thread().name))
def thread_run(name):
localSet.student = name
func_run()
if __name__ == '__main__':
t1 = threading.Thread( target=thread_run , args=( 'lrh' , ) , name= 'Thread-A')
t2 = threading.Thread( target=thread_run , args=( 'kch' ,) , name= 'Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()