资讯专栏INFORMATION COLUMN

Python多线程

dcr309duan / 3216人阅读

摘要:多线程的理解多进程和多线程都可以执行多个任务,线程是进程的一部分。多线程创建在中,同样可以实现多线程,有两个标准模块和,不过我们主要使用更高级的模块。多线程的应用场景。

1、多线程的理解

多进程和多线程都可以执行多个任务,线程是进程的一部分。线程的特点是线程之间可以共享内存和变量,资源消耗少(不过在Unix环境中,多进程和多线程资源调度消耗差距不明显,Unix调度较快),缺点是线程之间的同步和加锁比较麻烦。

2、Python多线程创建

在Python中,同样可以实现多线程,有两个标准模块thread和threading,不过我们主要使用更高级的threading模块。使用例子:

import threading
import time

def target():
    print "the curent threading  %s is running" % threading.current_thread().name
    time.sleep(1)
    print "the curent threading  %s is ended" % threading.current_thread().name

print "the curent threading  %s is running" % threading.current_thread().name
t = threading.Thread(target=target)

t.start()
t.join()
print "the curent threading  %s is ended" % threading.current_thread().name

输出:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading Thread-1 is ended
the curent threading MainThread is ended

start是启动线程,join是阻塞当前线程,即使得在当前线程结束时,不会退出。从结果可以看到,主线程直到Thread-1结束之后才结束。
Python中,默认情况下,如果不加join语句,那么主线程不会等到当前线程结束才结束,但却不会立即杀死该线程。如不加join输出如下:

the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading MainThread is ended
the curent threading Thread-1 is ended

但如果为线程实例添加t.setDaemon(True)之后,如果不加join语句,那么当主线程结束之后,会杀死子线程。代码:

import threading
import time
def target():
    print "the curent threading  %s is running" % threading.current_thread().name
    time.sleep(4)
    print "the curent threading  %s is ended" % threading.current_thread().name
print "the curent threading  %s is running" % threading.current_thread().name
t = threading.Thread(target=target)
t.setDaemon(True)
t.start()
t.join()
print "the curent threading  %s is ended" % threading.current_thread().name

输出如下:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading MainThread is ended
如果加上join,并设置等待时间,就会等待线程一段时间再退出:

import threading
import time
def target():
    print "the curent threading  %s is running" % threading.current_thread().name
    time.sleep(4)
    print "the curent threading  %s is ended" % threading.current_thread().name
print "the curent threading  %s is running" % threading.current_thread().name
t = threading.Thread(target=target)
t.setDaemon(True)
t.start()
t.join(1)

输出:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading MainThread is ended
主线程等待1秒,就自动结束,并杀死子线程。如果join不加等待时间,t.join(),就会一直等待,一直到子线程结束,输出如下:
the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading Thread-1 is ended
the curent threading MainThread is ended

3、线程锁和ThreadLocal (1)线程锁

对于多线程来说,最大的特点就是线程之间可以共享数据,那么共享数据就会出现多线程同时更改一个变量,使用同样的资源,而出现死锁、数据错乱等情况。

假设有两个全局资源,a和b,有两个线程thread1,thread2. thread1占用a,想访问b,但此时thread2占用b,想访问a,两个线程都不释放此时拥有的资源,那么就会造成死锁。

对于该问题,出现了Lock。 当访问某个资源之前,用Lock.acquire()锁住资源,访问之后,用Lock.release()释放资源。

import threading
import time

a = 3
lock = threading.Lock()
def target():
    print "the curent threading  %s is running" % threading.current_thread().name
    time.sleep(4)
    global a #使用global语句可以清楚地表明变量是在外面的块定义的
    lock.acquire()
    try:
        a += 3
    finally:
        lock.release()
    print "the curent threading  %s is ended" % threading.current_thread().name
    print "yes"
t = threading.Thread(target=target)
t1 = threading.Thread(target=target)

t.start()
t1.start()
t.join()
t1.join()
print a

用finally的目的是防止当前线程无线占用资源。

(2)ThreadLocal

介绍完线程锁,接下来出场的是ThreadLocal。当不想将变量共享给其他线程时,可以使用局部变量,但在函数中定义局部变量会使得在函数之间传递特别麻烦。ThreadLocal是非常牛逼的东西,它解决了全局变量需要枷锁,局部变量传递麻烦的两个问题。通过在线程中定义:
local_school = threading.local()
此时这个local_school就变成了一个全局变量,但这个全局变量只在该线程中为全局变量,对于其他线程来说是局部变量,别的线程不可更改。 def process_thread(name):# 绑定ThreadLocal的student: local_school.student = name

这个student属性只有本线程可以修改,别的线程不可以。代码:

local = threading.local()
def func(name):
    print "current thread:%s" % threading.currentThread().name
    local.name = name
    print "%s in %s" % (local.name,threading.currentThread().name)
t1 = threading.Thread(target=func,args=("haibo",))
t2 = threading.Thread(target=func,args=("lina",))
t1.start()
t2.start()
t1.join()
t2.join()

从代码中也可以看到,可以将ThreadLocal理解成一个dict,可以绑定不同变量。
ThreadLocal用的最多的地方就是每一个线程处理一个HTTP请求,在Flask框架中利用的就是该原理,它使用的是基于Werkzeug的LocalStack。

4、Map实现多线程:

对于多线程的使用,我们经常是用thread来创建,比较繁琐:

class MyThread(threading.Thread):
    def init(self):
        threading.Thread.init(self)
def run(self):
    lock.acquire()
    print threading.currentThread().getName()
    lock.release()
 
def build_worker(num):
    workers = []
    for t in range(num):
        work = MyThread()
        work.start()
        workers.append(work)
    return workers
def producer():
    threads = build_worker(4)
    for w in threads:
        w.join()
    print "Done"

如果要创建更多的线程,那就要一一加到里面,操作麻烦,代码可读性也变差。在Python中,可以使用map函数简化代码。map可以实现多任务的并发,简单示例:

urls = ["http://www.baidu.com","http://www.sina.com","http://www.qq.com"]
results=map(urllib2.urlopen,urls)

urls = ["http://www.baidu.com","http://www.sina.com","http://www.qq.com"]
results=map(urllib2.urlopen,urls)
map将urls的每个元素当做参数分别传给urllib2.urlopen函数,并最后把结果放到results列表中,map 函数一手包办了序列操作、参数传递和结果保存等一系列的操作。 其原理:

map函数负责将线程分给不同的CPU。

在 Python 中有个两个库包含了 map 函数: multiprocessing 和它鲜为人知的子库 multiprocessing.dummy.dummy 是 multiprocessing 模块的完整克隆,唯一的不同在于 multiprocessing 作用于进程,而 dummy 模块作用于线程。代码:

import urllib2

from multiprocessing.dummy import Pool as ThreadPool

urls = ["http://www.baidu.com","http://www.sina.com","http://www.qq.com"]

pool = ThreadPool()

results = pool.map(urllib2.urlopen,urls)
print results
pool.close()
pool.join()

print "main ended"
5、Python多线程的缺陷:

上面说了那么多关于多线程的用法,但Python多线程并不能真正能发挥作用,因为在Python中,有一个GIL,即全局解释锁,该锁的存在保证在同一个时间只能有一个线程执行任务,也就是多线程并不是真正的并发,只是交替得执行。假如有10个线程炮在10核CPU上,当前工作的也只能是一个CPU上的线程。

6、Python多线程的应用场景。

虽然Python多线程有缺陷,总被人说成是鸡肋,但也不是一无用处,它很适合用在IO密集型任务中。I/O密集型执行期间大部分是时间都用在I/O上,如数据库I/O,较少时间用在CPU计算上。因此该应用场景可以使用Python多线程,当一个任务阻塞在IO操作上时,我们可以立即切换执行其他线程上执行其他IO操作请求。

总结:Python多线程在IO密集型任务中还是很有用处的,而对于计算密集型任务,应该使用Python多进程。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41027.html

相关文章

  • 浅谈Python线程

    摘要:进程可创建多个线程来执行同一程序的不同部分。就绪等待线程调度。运行线程正常运行阻塞暂停运行,解除阻塞后进入状态重新等待调度。消亡线程方法执行完毕返回或者异常终止。多线程多的情况下,依次执行各线程的方法,前头一个结束了才能执行后面一个。 浅谈Python多线程 作者简介: 姓名:黄志成(小黄)博客: 博客 线程 一.什么是线程? 操作系统原理相关的书,基本都会提到一句很经典的话: 进程...

    zsirfs 评论0 收藏0
  • 如何编写快速且线程安全的Python代码

    摘要:其次,解释器的主循环,一个名为的函数,读取字节码并逐个执行其中的指令。所有线程都运行相同的代码,并以相同的方式定期从它们获取锁定。无论如何,其他线程无法并行运行。 概述 如今我也是使用Python写代码好多年了,但是我却很少关心GIL的内部机制,导致在写Python多线程程序的时候。今天我们就来看看CPython的源代码,探索一下GIL的源码,了解为什么Python里要存在这个GIL,...

    B0B0 评论0 收藏0
  • Python线程专题纵览篇

    摘要:上一篇文章进程专题完结篇多进程处理的一般建议下一篇文章线程专题多线程使用的必要性进程线程进程能够完成多任务,比如在一个电脑上可以运行多个软件。由于占用资源少,也使得多线程程序并发比较高。 上一篇文章:Python进程专题完结篇:多进程处理的一般建议下一篇文章:Python线程专题1:多线程使用的必要性 进程VS线程 进程:能够完成多任务,比如在一个电脑上可以运行多个软件。线程:也能够...

    张红新 评论0 收藏0
  • python学习笔记- 线程

    摘要:也提供多线程支持,而且中的线程并非是模拟出来的多线程,而是系统级别的标准库提供了两个模块和。同一个变量,线程则会互相共享。例如多个线程对银行中的某一个账户进行操作。但是实际情况是随意切换线程。说到的多线程编程,就会绕不过。 该文章参考了http://www.liaoxuefeng.com/wi... 廖雪峰的教程。 一个进程至少有一个线程。Python也提供多线程支持,而且Python...

    RiverLi 评论0 收藏0
  • # Python 线程和锁

    摘要:多线程和锁作者博客进程和线程进程是执行中的计算机程序。线程包括开始执行顺序和结束三部分。的多进程相关模块模块是高级别的多线程模块。线程锁当多线程争夺锁时,允许第一个获得锁的线程进入临街区,并执行代码。 Python 多线程和锁 作者博客:http://zzir.cn/ 进程和线程 进程是执行中的计算机程序。每个进程都拥有自己的地址空间、内存、数据栈及其它的辅助数据。操作系统管理着所有的...

    cpupro 评论0 收藏0

发表评论

0条评论

阅读需要支付1元查看
<