在对称多处理器SMP时代,Python多线程技术显得有些弱鸡。因为GIL的存在,Python多线程只能实现单核多线程功能,无法实现多核多线程。虽然GIL显得有些自费武功,但是多线程技术在IO密集型应用中仍然能够提升一部分系统性能。所以还是有必要学习一些Python多线程编程。
-
线程是什么?
在Linux系统中,线程又名轻量级进程(LWP),是操作系统最小调度单位。这个命题,咋看起来没问题,细细评味总觉得有点不准确,会给人产生一种错觉,认为Linux中最小调度单位是线程。
在Linux2.6版本之后的版本,内核线程实现方式基本采用NPTL。NPTL是一个1*1的线程库,就是说当你使用pthread_create方法创建一个线程后,在内核里就相应创建了一个调度实体,在Linux里就是一个新进程,并且描述这个实体的数据结构仍然是task_struct。看到这里,稍懂Linux进程编程的人会得出这样一个结论:Linux操作系统的调度单位从来没变,仍然是以进程为单位。更详细的线程介绍可以参考《深入理解Linux内核》。
-
线程有什么用?
在线程出生之前,只有进程。但是创建进程的代价有点高,所以为了弥补进程的这个缺陷,上帝发明了线程。线程的优点是多线程之间共享了父进程的大部分资源,同时又拥有属于自己的线程栈。这种特性使线程即少占用系统资源,又易于线程间通信。最终的结果是多线程极大提升了系统性能。
多线程编程
Python多线程编程,离不开内置的threading库。
1.继承threading.Thread类
import threading
class TestThread(threading.Thread):
def __init__(self):
super(TestThread,self).__init__() #必须调用父类
def run(self):
print self.name
2.使用函数调用方式
def pprint(log):
print log
thread = threading.Thread(target=pprint,args=('hello thread',))
thread.start()
3.设置守护线程
def pprint(log):
print log
if __name__ == '__main__':
thread = threading.Thread(target=pprint,args=('hello thread',))
thread.setDaemon(True)
thread.start()
守护线程的特点:主线程退出,守护线程也退出。
4.多线程共享全局变量,使用互斥锁对临界区进行保护
import threading
import time
count = 0
lock = threading.Lock()
def increment(name):
global count
with lock:
for i in range(3):
time.sleep(1)
print name
count += 1
if __name__ == '__main__':
for i in range(10):
thread = threading.Thread(target=increment,args=('thread-%s'%i,))
thread.start()
while True:
if threading.active_count() != 1:
continue
else:
break
print count
5.使用信号量
import threading
import time
def test(n,sem):
sem.acquire()
time.sleep(1)
print 'thread %s ' % n
sem.release()
if __name__ == '__main__':
sem = threading.BoundedSemaphore(4)
for i in range(10):
thread = threading.Thread(target=test,args=(i,sem,))
thread.start()
while True:
if threading.active_count() != 1:
continue
else:
break
信号量threading.BoundedSemaphore允许不止一个线程修改全局变量,而互斥锁任何时刻只允许一个线程修改全局变量。
6.事件
import threading
import time
event = threading.Event()
def test(n):
event.set()
while True:
time.sleep(1)
print 'thread %s print hello event\n' % n
if event.is_set():
break
if __name__ == '__main__':
sem = threading.BoundedSemaphore(4)
for i in range(5):
thread = threading.Thread(target=test,args=(i,))
thread.start()
time.sleep(5)
print 'main thread set event'
event.set()
事件可以用于一个线程控制其它线程的执行。常用的方法有:set(设置flag为True),is_set(判断是否设置了flag),clear(设置flag为False)和wait(监听flag,如果没有检测到flag则阻塞当前线程)
7.定时器
import threading
import time
def test():
time.sleep(1)
print 'hello timer'
if __name__ == '__main__':
thread = threading.Timer(1,test)
thread.start()
time.sleep(5)
print 'end'
定时器表示一个操作需要在等待一定时间之后执行。定时器方法有4个参数:
-
interval:间隔时间,即定时器秒数。
-
function:执行的函数。
-
args:传入function的参数,如果为None,则会传入一个空列表。
-
kwargs:传入function的关键字参数,如果为None,则会传入一个空字典。
8.栅栏(Python3支持)
import threading
import time
def test():
print('barrier')
barrier = threading.Barrier(3, test)
def barrier_thread(sleep):
time.sleep(sleep)
print('barrier thread-%s wait...' % sleep)
barrier.wait()
print('barrier thread-%s end!' % sleep)
for i in range(6):
threading.Thread(target=barrier_thread,args=(i,)).start()
输出:
barrier thread-0 wait...
barrier thread-1 wait...
barrier thread-2 wait...
barrier
barrier thread-2 end!
barrier thread-0 end!
barrier thread-1 end!
barrier thread-3 wait...
barrier thread-4 wait...
barrier thread-5 wait...
barrier
barrier thread-5 end!
barrier thread-4 end!
barrier thread-3 end!
栅栏对象用于一个固定数量的线程,而这些线程需要等待彼此的情况。这些线程中的每个线程都会尝试调用wait()方法,然后阻塞,直到所有线程都调用了wait()方法,然后所有线程会被同时释放。
小结
在多核系统中,Python多线程虽然只能利用单核,但是在IO密集型应用中仍然有必要使用。
如果对云计算感兴趣,可以关注我的微信公众号: