python 线程笔记(一)

1、一个顺序执行的程序要从每个I/O终端信道检查用户的输入时,程序无论如何也不能在读取I/O终端信道的时候阻塞,因为用户输入的到达是不确定的。

阻塞会导致其他I/O信息的数据部能被处理,顺序执行的程序必须使用非阻塞I/O,或是带有计时器的阻塞I/O(这样才能保证
阻塞只是暂时的)。  


2、  使用多线程编程和一个共享的数据结构如Queue (本章后面会介绍的一种多线程队列数据结构),
这种程序任务可以用几个功能单一的线程来组织:  
      UserRequestThread: 负责读取客户的输入,可能是一个I/O信道。程序可能创建多个线程,  每个客户一个,请求会被放入队列中。  
     RequestProcessor: 一个负责从队列中获取并处理请求的线程,它为下面那种线程提供输出。  
     ReplyThread: 负责把给用户的输出取出来,如果是网络应用程序就把结果发送出去,否则就保存到本地文件系统或数据库中。  


3、
计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据。它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期。进程 (有时被称为重量级进程)是程序的一次执行。每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据。操作系统管理在其上运行的所有进程,并为这些进程公平地分配时间。进程也可以通过fork和spawn操作 来完成其它的任务。不过各个进程有自己的内存空间,数据栈等,所以只能使用进程间通讯(IPC), 而不能直接共享信息。  


4、 线程有开始,顺序执行和结束三部分。它有一个自己的指令指针,记录自己运行到什么地方。线程的运行可能被抢占(中断),或暂时的被挂起(也叫睡眠),让其它的线程运行,这叫做让步。 一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据 以及相互通讯。线程一般都是并发执行的,正是由于这种并行和数据共享的机制使得多个任务的合 作变为可能。实际上,在单 CPU 的系统中,真正的并发是不可能的,每个线程会被安排成每次只运
行一小会,然后就把 CPU 让出来,让其它的线程去运行。在进程的整个运行过程中,每个线程都只 做自己的事,在需要的时候跟其它的线程共享运行的结果。  
       当然,这样的共享并不是完全没有危险的。如果多个线程共同访问同一片数据,则由于数据访 问的顺序不一样,有可能导致数据结果的不一致的问题。这叫做竞态条件(race condition)。幸运的是,大多数线程库都带有一系列的同步原语,来控制线程的执行和数据的访问。  
       另一个要注意的地方是,由于有的函数会在完成之前阻塞住,在没有特别为多线程做修改的情况下,这种“贪婪”的函数会让 CPU 的时间分配有所倾斜。导致各个线程分配到的运行时间可能不尽相同,不尽公平。  

5、 对 Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。在多线程环境中,Python虚拟机按以下方式执行:  
      
    1.   设置GIL  
    2.   切换到一个线程去运行  
    3.   运行:  
      a. 指定数量的字节码指令,或者  
      b. 线程主动让出控制(可以调用time.sleep(0))  
    4.   把线程设置为睡眠状态  
    5.   解锁GIL  
    6.   再次重复以上所有步骤 
6、 当一个线程结束计算,它就退出了。线程可以调用thread.exit()之类的退出函数,也可以使用Python 退出进程的标准方法,如 sys.exit()或抛出一个 SystemExit 异常等。不过,你不可以直接 “杀掉”("kill")一个线程。  

7、 核心提示:避免使用 thread 模块  
    出于以下几点考虑,我们不建议您使用 thread 模块。首先,更高级别的 threading 模块更为先进,对线程的支持更为完善,而且使用 thread 模块里的属性有可能会与 threading 出现冲突。其次,低级别的 thread 模块的同步原语很少(实际上只有一个),而 threading 模块则有很多。  

8、thread模块和锁对象  
      
    函数                               描述  
    thread模块函数  
    start_new_thread(function,   args, kwargs=None)              产生一个新的线程,在新线程中用指定的参数和可选的
                                  kwargs来调用这个函数。  
    allocate_lock()                  分配一个LockType类型的锁对象  
    exit()                          让线程退出  


    LockType类型锁对象方法  
    acquire(wait=None)              尝试获取锁对象  
    locked()                        如果获取了锁对象返回True,否则返回False  
    release()                         释放锁 

9、使用线程和锁
      
      
  

import thread  
from time import sleep, ctime  
  
loops = [4,2]  
  
def loop(nloop, nsec, lock):
    print 'start loop', nloop, 'at:', ctime()  
    sleep(nsec)  
    print 'loop', nloop, 'done at:', ctime()  
    lock.release()  

def main():  
    print 'starting at:', ctime()  
    locks = []  
    nloops = range(len(loops))  
       
    for i in nloops:  
        lock = thread.allocate_lock()  
        lock.acquire()  
        locks.append(lock)  
  
    for i in nloops:  
        thread.start_new_thread(loop, (i, loops[i], locks[i]))  
      
    for i in nloops:  
        while locks[i].locked(): pass  
    
    print 'all DONE at:', ctime()  
if __name__ == "__main__":
    main() 

运行结果:

>>> starting at: Sat Nov 03 20:02:34 2012
start loop 0 at: Sat Nov 03 20:02:34 2012
start loop 1 at: Sat Nov 03 20:02:34 2012
loop 1 done at: Sat Nov 03 20:02:36 2012
loop 0 done at: Sat Nov 03 20:02:38 2012
all DONE at: Sat Nov 03 20:02:38 2012

主要的工作在包含三个循环的main()函数中完成。我们先调用thread.allocate_lock()函数创建一个锁的列表,并分别调用各个锁的 acquire()函数获得锁。获得锁表示“把锁锁上”。锁上后,我们就把锁放到锁列表 locks 中。下一个循环创建线程,每个线程都用各自的循环号,睡眠时间和锁为参数去调用loop()函数。
在线程结束的时候,线程要自己去做解锁操作。最后一个循环只是坐在那一直等(达到暂停主线程的目的),直到两个锁都被解锁为止才继续运行。由于我们顺序检查每一个锁,所以我们可能会要长时间地等待运行时间长且放在前面的线程,当这些线程的锁释放之后,后面的锁可能早就释放了(表示对应的线程已经运行完了)。结果主线程只能毫不停歇地完成对后面这些锁的检查。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值