python 单线程和多线程

来源:互联网 发布:红警网络卡怎么设置 编辑:程序博客网 时间:2024/05/17 03:03

单线程, 在好些年前的MS-DOS时代,操作系统处理问题都是单任务的,我想做听音乐和看电影两件事儿,那么一定要先排一下顺序。

#coding=utf-8import threadingfrom time import ctime,sleepdef music(func):    for i in range(2):        print "I was listening to %s. %s" %(func,ctime())        sleep(1)def movie(func):    for i in range(2):        print "I was at the %s! %s" %(func,ctime())        sleep(5)if __name__ == '__main__':    music(u'爱情买卖')    movie(u'阿凡达')    print "all over %s" %ctime()

一个任务只能等待上一个任务完成,才能执行。是阻塞的。

I was listening to 爱情买卖. Mon Feb 27 16:09:30 2017I was listening to 爱情买卖. Mon Feb 27 16:09:31 2017I was at the 阿凡达! Mon Feb 27 16:09:32 2017I was at the 阿凡达! Mon Feb 27 16:09:37 2017all over Mon Feb 27 16:09:42 2017

多线程,python提供了两个模块来实现多线程thread 和threading ,thread 有一些缺点,在threading 得到了弥补,为了不浪费你和时间,所以我们直接学习threading 就可以了。

#coding=utf-8import threadingfrom time import ctime,sleepdef music(func):    for i in range(2):        print "I was listening to %s. %s" %(func,ctime())        sleep(1)def movie(func):    for i in range(2):        print "I was at the %s! %s" %(func,ctime())        sleep(5)threads = []t1 = threading.Thread(target=music,args=(u'爱情买卖',))threads.append(t1)t2 = threading.Thread(target=movie,args=(u'阿凡达',))threads.append(t2)if __name__ == '__main__':    for t in threads:        # setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句print "all over %s" %ctime()后,没有等待子线程,直接就退出了,同时子线程也一同结束。需要加一句t.join(),才会等待子线程。        t.setDaemon(True)        t.start()    # join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。    t.join()    print "all over %s" %ctime()
I was listening to 爱情买卖. Mon Feb 27 16:14:36 2017I was at the 阿凡达! Mon Feb 27 16:14:36 2017I was listening to 爱情买卖. Mon Feb 27 16:14:37 2017I was at the 阿凡达! Mon Feb 27 16:14:41 2017all over Mon Feb 27 16:14:46 2017

从执行结果可看到,music 和move 是同时启动的。

开始时间36秒,直到调用主进程为46,总耗时为10秒。从单线程时减少了2秒。

这里再聊聊,进程与线程的区别。

对于操作系统来说,一个任务就是一个进程。例如打开浏览器,打开word,打开记事本等等,都是独立的任务,它们各自为一个或者多个进程。这里要注意的是,同一种任务打开多个,分别属于不同进程,例如chrome打开多个标签,实际上它创建了多个进程。

对于一个任务来说,它有很多子任务,例如播放器,既要解码视频、也要解码音频,所以在进程下存在多线程。在一个进程下一定存在一个线程,可以称它为主线程。

操作系统创建进程时,会单独为每一个进程分配各自的资源,进程与进程之间相互隔离。而进程内的线程,则共享了当前进程内的资源。可见,操作系统执行的粒度是线程,分配资源的粒度是进程,我们的多任务操作系统,在单核CPU上是在各个线程上不断切换而达到目的,而在多核CPU上则能同时执行多个线程任务。

概念上来说,多进程并发即运行多个独立的程序,优势在于并发处理的任务都由操作系统管理,不足之处在于程序与各进程之间的通信和数据共享不方便。
多线程并发则由程序员管理并发处理的任务,这种并发方式可以方便地在线程间共享数据(前提是不能互斥)。

在linux中,每个进程都是由父进程提供的。每启动一个子进程就从父进程克隆一份数据,但是进程之间的数据本身是不能共享的。
在一般情况下多个进程的内存资源是相互独立的,而多线程可以共享同一个进程中的内存资源。多进程之间的通信通过Queue()或Pipe()来实现。

GIL锁( Global Interpreter Lock )
Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。

所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。

不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

参考:
http://www.cnblogs.com/fnng/p/3670789.html
http://www.wyblog.cn/2016/12/05/python%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%A4%9A%E8%BF%9B%E7%A8%8B/
http://www.tuicool.com/articles/YJvMjmb

0 0
原创粉丝点击