python学习笔记- 多线程(1)

学习多线程首先先要理解线程和进程的关系。

进程

计算机的程序是储存在磁盘中的可执行的二进制文件,执行时把这些二进制文件加载到内存中,操作系统调用并交给处理器执行对应操作,进程是程序的一次执行过程,这是一个动态的概念程序本没有生命的实体,只有通过处理器才能赋予程序生命。进程最大的特点就是可以申请和拥有系统资源,这是一个动态的概念,是一个活动的实体(比如qq程序需要的内存空间等等),进程不止是程序代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。

而线程是进程的一部分,一个进程可以包含多个线程在运行,线程拥有的资源是进程聪操作系统申请并拥有的资源,在引入进程的操作系统通常是把该线程所属的进程作为线程分配资源的基本单位,从而线程基本不拥有系统资源。

那么可想而知线程和进程的最大区别就是子进程和父线程拥有不同的代码和数据空间,而进程中的多个线程资源是共享的,所以进程的上下文切换要比进程快得多。多线程主要是为了节约cpu运行的时间。

总而言之进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

单线程

单线程的运行是顺序运行的,比如同时执行两个不嵌套的循环,第一个执行完第二个循环才能执行,程序执行时间基本为两个循环的总时间

这个程序一共耗时8s。

 

我用的是python 2.7,Ubuntu自带2.7

python封装了两个类库从而对多线程的支持:thread和threading.thread是比较低级别的、原始的线程以及一个简单的锁。threading提供了Rlock和Condition(下文会详细介绍)

 

thread模块

 

thread模块提供了start_new_thread(function,args,kwargs)方法来开启一个新线程 ,由于前两个参数是必需的,所以第二个参数为空也必需传一个空的元祖,函数申明如下图:

 

这个程序执行的时间是所有同时执行的线程的最长的进程时间6s,即主线程的6s。那么为什么要让主线程sleep6s呢?那是因为我们知道开启的两个子线程都是4s,让主线程休眠6s刚好可以等到子线程执行完毕,如果没有sleep 6s这句代码,那么主线程会直接往下执行,直至主线程结束,那么会导致子线程还没有执行完毕也随着主线程的结束而结束,而且主线程休眠6s,而子线程4s就执行完毕,那么这样主线程晚退出了2s。

上面是我们人为手动让主线程休眠6s,可能会导致主线程过早或过晚的退出,那么如何去实现这个动态的过程。

上面使用了锁实现了这一需求。

 

threading模块

  上面已经可以很清楚的看出thread模块的局限,当主线程退出时,所有的子线程无论是否已经执行完毕都要被强行退出,于是就有了守护线程的概念,threading模块则支持守护线程。

调用threading模块的threading.Thread(fun,agr)的工厂方法生成实例。在调用start方法开启线程,在对每个线程调用join方法。join方法会让这个主线程等待子线程的终止或者达到在给了timeout参数线程执行超时结束,不然主线程不会结束退出。

 

使用多线程实现数据同步

使用多线程时很容易出现下面的问题。

假设两个线程对象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描述了多线程情况下最常见的问题之一:数据共享。当多个线程都要去修改某一个共享数据的时候,我们需要对数据访问进行同步。

最简单的同步机制就是“锁”。锁对象由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()  

  如上就简单的实现各个线程的数据共享。

posted on 2016-08-25 14:07  njfan  阅读(294)  评论(0编辑  收藏  举报

导航