Python多线程

本文详细介绍了Python多线程编程,包括线程的概念、作用,以及如何使用`threading`库创建和管理线程。尽管GIL限制了Python在多核环境下的并行计算能力,但在IO密集型任务中,多线程仍能提高系统性能。文章通过实例展示了线程的创建、线程间通信、同步机制如锁、信号量、事件和定时器的使用,以及栅栏等高级特性。
摘要由CSDN通过智能技术生成

在对称多处理器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密集型应用中仍然有必要使用。

 

如果对云计算感兴趣,可以关注我的微信公众号:

 

2020/5月/15好上传最新版 JavaGuide 目前已经 70k+ Star ,目前已经是所有 Java 类别项目中 Star 数量第二的开源项目了。Star虽然很多,但是价值远远比不上 Dubbo 这些开源项目,希望以后可以多出现一些这样的国产开源项目。国产开源项目!加油!奥利给! 随着越来越多的人参与完善这个项目,这个专注 “Java知识总结+面试指南 ” 项目的知识体系和内容的不断完善。JavaGuide 目前包括下面这两部分内容: Java 核心知识总结; 面试方向:面试题、面试经验、备战面试系列文章以及面试真实体验系列文章 内容的庞大让JavaGuide 显的有一点臃肿。所以,我决定将专门为 Java 面试所写的文章以及来自读者投稿的文章整理成 《JavaGuide面试突击版》 系列,同时也为了更加方便大家阅读查阅。起这个名字也犹豫了很久,大家如果有更好的名字的话也可以向我建议。暂时的定位是将其作为 PDF 电子书,并不会像 JavaGuide 提供在线阅读版本。我之前也免费分享过PDF 版本的《Java面试突击》,期间一共更新了 3 个版本,但是由于后面难以同步和订正所以就没有再更新。《JavaGuide面试突击版》 pdf 版由于我工作流程的转变可以有效避免这个问题。 另外,这段时间,向我提这个建议的读者也不是一个两个,我自己当然也有这个感觉。只是自己一直没有抽出时间去做罢了!毕竟这算是一个比较耗费时间的工程。加油!奥利给! 这件事情具体耗费时间的地方是内容的排版优化(为了方便导出PDF生成目录),导出 PDF 我是通过 Typora 来做的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值