`
zoulc001
  • 浏览: 30474 次
  • 性别: Icon_minigender_1
  • 来自: 成都
文章分类
社区版块
存档分类
最新评论

【转】Python多线程学习

阅读更多

 一、Python 中的线程使用:

     Python 中使用线程有两种方式:函数或者用类来包装线程对象。

1、   函数式:调用 thread 模块中的 start_new_thread() 函数来产生新线程。如下例:

import time  
import thread  
def timer(no, interval):  
    cnt = 0  
    while cnt<10:  
        print 'Thread:(%d) Time:%s\n'%(no, time.ctime())  
        time.sleep(interval)  
        cnt+=1  
    thread.exit_thread()  
         
       
def test(): #Use thread.start_new_thread() to create 2 new threads  
    thread.start_new_thread(timer, (1,1))  
    thread.start_new_thread(timer, (2,2))  
if __name__=='__main__':  
    test()  
 

上面的例子定义了一个线程函数 timer, 它打印出 10 条时间记录后退出,每次打印的间隔由 interval 参数决定。 thread.start_new_thread(function, args[, kwargs]) 的第一个参数是线程函数(本例中的 timer 方法),第二个参数是传递给线程函数的参数,它必须是 tuple 类型, kwargs 是可选参数。

     线程的结束可以等待线程自然结束,也可以在线程函数中调用 thread.exit() thread.exit_thread() 方法。

2、   创建 threading.Thread 的子类来包装一个线程对象,如下例:

import threading
import time
class timer(threading.Thread): #The timer class is derived from the class threading.Thread
    def __init__(self, num, interval):
        threading.Thread.__init__(self)
        self.thread_num = num
        self.interval = interval
        self.thread_stop = False
 
    def run(self): #Overwrite run() method, put what you want the thread do here
        while not self.thread_stop:
            print 'Thread Object(%d), Time:%s/n' %(self.thread_num, time.ctime())
            time.sleep(self.interval)
    def stop(self):
        self.thread_stop = True
       
 
def test():
    thread1 = timer(1, 1)
    thread2 = timer(2, 2)
    thread1.start()
    thread2.start()
    time.sleep(10)
    thread1.stop()
    thread2.stop()
    return
 
if __name__ == '__main__':
    test()
 

     就我个人而言,比较喜欢第二种方式,即创建自己的线程类,必要时重写 threading.Thread 类的方法,线程的控制可以由自己定制。

threading.Thread 类的使用:

1 ,在自己的线程类的 __init__ 里调用 threading.Thread.__init__(self, name = threadname)

Threadname 为线程的名字

2  run() ,通常需要重写,编写代码实现做需要的功能。

3 getName() ,获得线程对象名称

4 setName() ,设置线程对象名称

5 start() ,启动线程

6 jion([timeout]) ,等待另一线程结束后再运行。

7 setDaemon(bool) ,设置子线程是否随主线程一起结束,必须在 start() 之前调用。默认为 False

8 isDaemon() ,判断线程是否随主线程一起结束。

9 isAlive() ,检查线程是否在运行中。

     此外 threading 模块本身也提供了很多方法和其他的类,可以帮助我们更好的使用和管理线程。可以参看 http://www.python.org/doc/2.5.2/lib/module-threading.html


假设两个线程对象 t1 t2 都要对 num=0 进行增 1 运算, t1 t2 都各对 num 修改 10 次, num 的最终的结果应该为 20 。但是由于是多线程访问,有可能出现下面情况:在 num=0 时, t1 取得 num=0 。系统此时把 t1 调度为 ”sleeping” 状态,把 t2 转换为 ”running” 状态, t2 页获得 num=0 。然后 t2 对得到的值进行加 1 并赋给 num ,使得 num=1 。然后系统又把 t2 调度为 ”sleeping” ,把 t1 转为 ”running” 。线程 t1 又把它之前得到的 0 1 后赋值给 num 。这样,明明 t1 t2 都完成了 1 次加 1 工作,但结果仍然是 num=1

     上面的 case 描述了多线程情况下最常见的问题之一:数据共享。当多个线程都要去修改某一个共享数据的时候,我们需要对数据访问进行同步。

1、    简单的同步

最简单的同步机制就是“锁”。锁对象由 threading.RLock 类创建。线程可以使用锁的 acquire() 方法获得锁,这样锁就进入“ locked ”状态。每次只有一个线程可以获得锁。如果当另一个线程试图获得这个锁的时候,就会被系统变为“ blocked ”状态,直到那个拥有锁的线程调用锁的 release() 方法来释放锁,这样锁就会进入“ unlocked ”状态。“ blocked ”状态的线程就会收到一个通知,并有权利获得锁。如果多个线程处于“ blocked ”状态,所有线程都会先解除“ blocked ”状态,然后系统选择一个线程来获得锁,其他的线程继续沉默(“ blocked ”)。

Python 中的 thread 模块和 Lock 对象是 Python 提供的低级线程控制工具,使用起来非常简单。如下例所示:

import thread
import time
mylock = thread.allocate_lock()  #Allocate a lock
num=0  #Shared resource

def add_num(name):
    global num
    while True:
        mylock.acquire() #Get the lock 
        # Do something to the shared resource
        print 'Thread %s locked! num=%s'%(name,str(num))
        if num >= 5:
            print 'Thread %s released! num=%s'%(name,str(num))
            mylock.release()
            thread.exit_thread()
        num+=1
        print 'Thread %s released! num=%s'%(name,str(num))
        mylock.release()  #Release the lock.

def test():
    thread.start_new_thread(add_num, ('A',))
    thread.start_new_thread(add_num, ('B',))

if __name__== '__main__':
    test()
 

Python  thread 的基础上还提供了一个高级的线程控制库,就是之前提到过的 threading Python threading module 是在建立在 thread module 基础之上的一个 module ,在 threading module 中,暴露了许多 thread module 中的属性。在 thread module 中, python 提供了用户级的线程同步工具“ Lock ”对象。而在 threading module 中, python 又提供了 Lock 对象的变种 : RLock 对象。 RLock 对象内部维护着一个 Lock 对象,它是一种可重入的对象。对于 Lock 对象而言,如果一个线程连续两次进行 acquire 操作,那么由于第一次 acquire 之后没有 release ,第二次 acquire 将挂起线程。这会导致 Lock 对象永远不会 release ,使得线程死锁。 RLock 对象允许一个线程多次对其进行 acquire 操作,因为在其内部通过一个 counter 变量维护着线程 acquire 的次数。而且每一次的 acquire 操作必须有一个 release 操作与之对应,在所有的 release 操作完成之后,别的线程才能申请该 RLock 对象。

下面来看看如何使用 threading RLock 对象实现同步。

import threading
mylock = threading.RLock()
num=0
 
class myThread(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.t_name = name
        
    def run(self):
        global num
        while True:
            mylock.acquire()
            print '/nThread(%s) locked, Number: %d'%(self.t_name, num)
            if num>=4:
                mylock.release()
                print '/nThread(%s) released, Number: %d'%(self.t_name, num)
                break
            num+=1
            print '/nThread(%s) released, Number: %d'%(self.t_name, num)
            mylock.release()
            
def test():
    thread1 = myThread('A')
    thread2 = myThread('B')
    thread1.start()
    thread2.start()
 
if __name__== '__main__':
    test()
 

我们把修改共享数据的代码成为“临界区”。必须将所有“临界区”都封闭在同一个锁对象的 acquire release 之间。

2、    条件同步

锁只能提供最基本的同步。假如只在发生某些事件时才访问一个“临界区”,这时需要使用条件变量 Condition

Condition 对象是对 Lock 对象的包装,在创建 Condition 对象时,其构造函数需要一个 Lock 对象作为参数,如果没有这个 Lock 对象参数, Condition 将在内部自行创建一个 Rlock 对象。在 Condition 对象上,当然也可以调用 acquire release 操作,因为内部的 Lock 对象本身就支持这些操作。但是 Condition 的价值在于其提供的 wait notify 的语义。

条件变量是如何工作的呢?首先一个线程成功获得一个条件变量后,调用此条件变量的 wait() 方法会导致这个线程释放这个锁,并进入“ blocked ”状态,直到另一个线程调用同一个条件变量的 notify() 方法来唤醒那个进入“ blocked ”状态的线程。如果调用这个条件变量的 notifyAll() 方法的话就会唤醒所有的在等待的线程。

如果程序或者线程永远处于“ blocked ”状态的话,就会发生死锁。所以如果使用了锁、条件变量等同步机制的话,一定要注意仔细检查,防止死锁情况的发生。对于可能产生异常的临界区要使用异常处理机制中的 finally 子句来保证释放锁。等待一个条件变量的线程必须用 notify() 方法显式的唤醒,否则就永远沉默。保证每一个 wait() 方法调用都有一个相对应的 notify() 调用,当然也可以调用 notifyAll() 方法以防万一。


生产者与消费者问题是典型的同步问题。这里简单介绍两种不同的实现方法。

1,   条件变量

import threading

import time

class Producer(threading.Thread):

    def __init__(self, t_name):

        threading.Thread.__init__(self, name=t_name)

 

    def run(self):

        global x

        con.acquire()

        if x > 0:

            con.wait()

        else:

            for i in range(5):

                x=x+1

                print "producing..." + str(x)

            con.notify()

        print x

        con.release()

 

class Consumer(threading.Thread):

    def __init__(self, t_name):

        threading.Thread.__init__(self, name=t_name)

    def run(self):

        global x

        con.acquire()

        if x == 0:

            print 'consumer wait1'

            con.wait()

        else:

            for i in range(5):

                x=x-1

                print "consuming..." + str(x)

            con.notify()

        print x

        con.release()

 

con = threading.Condition()

x=0

print 'start consumer'

c=Consumer('consumer')

print 'start producer'

p=Producer('producer')

 

p.start()

c.start()

p.join()

c.join()

print x

 

   上面的例子中,在初始状态下, Consumer 处于 wait 状态, Producer 连续生产(对 x 执行增 1 操作) 5 次后, notify 正在等待的 Consumer Consumer 被唤醒开始消费(对 x 执行减 1 操作)

2,   同步队列

Python 中的 Queue 对象也提供了对线程同步的支持。使用 Queue 对象可以实现多个生产者和多个消费者形成的 FIFO 的队列。

生产者将数据依次存入队列,消费者依次从队列中取出数据。

# producer_consumer_queue

from Queue import Queue

import random

import threading

import time

 

#Producer thread

class Producer(threading.Thread):

    def __init__(self, t_name, queue):

        threading.Thread.__init__(self, name=t_name)

        self.data=queue

    def run(self):

        for i in range(5):

            print "%s: %s is producing %d to the queue!/n" %(time.ctime(), self.getName(), i)

            self.data.put(i)

            time.sleep(random.randrange(10)/5)

        print "%s: %s finished!" %(time.ctime(), self.getName())

 

#Consumer thread

class Consumer(threading.Thread):

    def __init__(self, t_name, queue):

        threading.Thread.__init__(self, name=t_name)

        self.data=queue

    def run(self):

        for i in range(5):

            val = self.data.get()

            print "%s: %s is consuming. %d in the queue is consumed!/n" %(time.ctime(), self.getName(), val)

            time.sleep(random.randrange(10))

        print "%s: %s finished!" %(time.ctime(), self.getName())

 

#Main thread

def main():

    queue = Queue()

    producer = Producer('Pro.', queue)

    consumer = Consumer('Con.', queue)

    producer.start()

    consumer.start()

    producer.join()

    consumer.join()

    print 'All threads terminate!'

 

if __name__ == '__main__':

    main()

 

在上面的例子中, Producer 在随机的时间内生产一个“产品”,放入队列中。 Consumer 发现队列中有了“产品”,就去消费它。本例中,由于 Producer 生产的速度快于 Consumer 消费的速度,所以往往 Producer 生产好几个“产品”后, Consumer 才消费一个产品。

Queue 模块实现了一个支持多 producer 和多 consumer FIFO 队列。当共享信息需要安全的在多线程之间交换时, Queue 非常有用。 Queue 的默认长度是无限的,但是可以设置其构造函数的 maxsize 参数来设定其长度。 Queue put 方法在队尾插入,该方法的原型是:

put(  item[, block[, timeout]])

如果可选参数 block true 并且 timeout None (缺省值),线程被 block ,直到队列空出一个数据单元。如果 timeout 大于 0 ,在 timeout 的时间内,仍然没有可用的数据单元, Full exception 被抛出。反之,如果 block 参数为 false (忽略 timeout 参数), item 被立即加入到空闲数据单元中,如果没有空闲数据单元, Full exception 被抛出。

Queue get 方法是从队首取数据,其参数和 put 方法一样。如果 block 参数为 true timeout None (缺省值),线程被 block ,直到队列中有数据。如果 timeout 大于 0 ,在 timeout 时间内,仍然没有可取数据, Empty exception 被抛出。反之,如果 block 参数为 false (忽略 timeout 参数),队列中的数据被立即取出。如果此时没有可取数据, Empty exception 也会被抛出。

 

 

分享到:
评论

相关推荐

    python多线程Python多线程学习Python中的线程使用

    python,python多线程Python多线程学习Python中的线程使用python多线程Python多线程学习Python中的线程使用

    Python多线程学习

    ### Python多线程学习 在Python中,使用线程主要有两种方法:一是通过函数的方式,二是利用类来封装线程对象。这两种方式都是基于Python的标准库`thread`和`threading`来实现的。 #### 函数方式创建线程 使用`...

    python多线程学习

    Python中的多线程是并发执行任务的一种方式,它允许程序同时处理多个任务,提升程序的效率。在Python中,我们通常使用`threading`模块来实现多线程,而不是`thread`模块,因为`threading`模块提供了更高级别的功能和...

    Python多线程编程(6寸)[归纳].pdf

    Python多线程编程是利用Python实现程序并行性的一种方式,尤其适合于处理异步、并发事务和资源密集型任务。在多线程环境中,多个线程可以同时执行,提高程序效率,尤其对于那些需要从多个输入源处理数据或者进行大量...

    单线程与多线程python爬虫地图瓦片源码

    本文将深入探讨“单线程与多线程Python爬虫地图瓦片源码”的相关知识点。 首先,我们需要理解“线程”这一概念。线程是程序执行的最小单元,每个线程负责执行特定的任务。在单线程环境中,程序按顺序执行,一次只能...

    PYthon-multithreading-Test.rar_python_python 多线程_python多线程_多线程

    通过深入学习和实践压缩包中的“PYthon multithreading Test”源码,你可以更好地掌握Python多线程的原理和应用,为编写高效、稳定的多线程程序打下坚实的基础。在实际开发中,结合具体场景选择合适的并发模型,是...

    浅析Python多线程与多进程的使用

    在多线程编程中,我们需要注意全局解释器锁(GIL),它是Python解释器为了保证线程安全而引入的一个机制,但同时也限制了多线程在CPU密集型任务中的性能,因为GIL使得Python在同一时刻只能有一个线程在执行。...

    Python多线程学习教程

    同时,了解GIL(全局解释器锁)在Python中的作用,以及它如何限制了Python多线程在CPU密集型任务上的性能提升,也是深入学习的一部分。 在实际应用中,Python的多线程更适合于I/O密集型任务,因为GIL在多线程处理...

    python多线程批量访问url脚本

    总的来说,这个"python多线程批量访问url脚本"提供了一个高效且易扩展的框架,用于批量处理HTTP请求,是学习Python网络编程和多线程处理的实用案例。通过理解和实践这个脚本,开发者不仅可以掌握多线程的基本概念,...

    完整版 Python高级开发课程 高级教程 08 Python多线程 多进程开发.pptx

    在Python高级开发中,多线程和多进程是两个重要的概念,它们被广泛应用于提高程序的并发性能,尤其是在处理大量数据或需要同时执行多个任务时。本课程将深入讲解这两个主题,帮助开发者提升Python应用程序的效率。 ...

    Python多线程编程详细示例

    这个是在学习python多线程的时候自己总结的文档,对理解Python多线程非常有帮助,很多都是从官方文档而来的。

    基于Linux的python多线程爬虫程序设计.zip

    最后,`基于Linux的python多线程爬虫程序设计.pdf`很可能包含详细的教程和示例代码,读者可以结合这份文档深入学习多线程爬虫的实现细节。通过实际项目练习,可以更好地理解和掌握这一技术,提升自己的编程能力。在...

    python多线程压缩包

    程序运行起来,叫进程,进程是资源分配的单位,线程执行代码,一个线程只能执行一个任务,想要执行多个任务,就需要多线程 协程依赖于线程,线程依赖于进程,协程切换需要资源相当少,所以效率就会很高 重构:把...

    第20章 Python多线程编程.pdf

    Python多线程编程是一种允许程序同时执行多...总的来说,Python多线程编程适合进阶型学习者,它要求开发者不仅要理解线程的工作原理,还要掌握如何正确使用`threading`模块提供的工具,实现高效且线程安全的程序设计。

    python 多线程编程

    通过分析和学习这个文件,你可以进一步加深对Python多线程编程的理解,包括如何有效地使用线程池(`ThreadPoolExecutor`),以及如何处理线程异常和线程间的协作问题。 总之,Python的多线程编程虽然受到GIL的限制...

    Python-python多线程函数库vthread简而强大

    "Python-python多线程函数库vthread简而强大"就是为了解决这一问题而诞生的。 vthread库是针对Python的一个增强型多线程库,它的主要目标是简化多线程和线程池的使用,提高开发效率。与Python标准库中的`threading`...

    python多线程

    Python多线程是指在Python编程语言中使用线程来执行多任务的技术。线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。多线程编程是并发编程的一部分,其目的是为了提高程序的...

    python多线程-threading模块.pdf

    Python 多线程 - Threading 模块 Python 中的多线程编程是使用 Threading 模块实现的,该模块提供了丰富的功能来创建和管理线程。在学习 Threading 模块之前,需要了解 Python 的基础知识,包括函数、类、对象等...

    基于python的多线程例子,详细介绍了多线程处理

    总的来说,通过学习和应用Python的多线程技术,我们可以编写出更加高效、响应迅速的程序。不过,需要注意线程安全、同步机制和资源管理,以确保程序的稳定性和正确性。在实际开发中,根据任务的性质选择合适的并发...

    Python多线程示例

    在这个"Python多线程示例"中,我们主要关注如何在Python环境中创建和管理线程。核心知识点包括: 1. **线程模块**:Python的`threading`模块提供了线程相关的所有功能。`Thread`类是其核心,通过实例化这个类可以...

Global site tag (gtag.js) - Google Analytics