18.2 线程和进程

        进程是程序的一次执行,拥有自己的地址空间、内存、数据栈及其他记录其运行轨迹的辅助数据。只能使用进程间通讯(IPC),而不能共享信息。

        线程,与进程有些相似。但所有线程都运行在同一个进程中。它有开始、顺序执行和结束三部分,也可能被中断、挂起。线程之间共享同一片数据空间。线程一般是并发执行的。

        18.3 python、线程和全局解释器锁

         全局解时器锁(GIL),对这个概念不是很理解。但它能保证同一时刻只有一个线程在运行。

         在多线程环境中,python虚拟机执行方式:

         1. 设置GIL;

         2. 切换到一个线程去运行;

         3. 运行:

                a. 指定数量的字节码的指令,或者

                b. 线程主动让出控制(可以调用time.sleep(0))

         4. 把线程设置成睡眠状态;

         5. 解锁GIL;

         6. 重复上述过程。

         之后是本章的所有例子。

onethr.py

#-*-coding: utf-8-*-

# 程序执行顺序是loop0-->loop1,在print "loop 0 done at:", ctime()执行完后,才开始执行loop1()。

from time import sleep, ctime

def loop0():
    print "start loop 0 at:", ctime()
    sleep(4)
    print "loop 0 done at:", ctime()

def loop1():
    print "start loop 1 at:", ctime()
    sleep(2)
    print "loop 1 done at:", ctime()

def main():
    print "starting at:", ctime()
    loop0()
    loop1()
    print "all DONE at:", ctime()

if __name__ == "__main__":
    main()

mtsleep1.py

#-*-coding: utf-8-*-

# 显然loop0和loop1同时运行,loop1先结束,然后loop0结束,之后再执行接下来的语句。
# sleep(6)正是让主线程休眠6s,而在这6s内,两个子线程分别将loop0()和loop1()运行完。

import thread
from time import sleep, ctime

def loop0():
    print "start loop 0 at:", ctime()
    sleep(4)
    print "loop 0 done at:", ctime()

def loop1():
    print "start loop 1 at:", ctime()
    sleep(2)
    print "loop 1 done at:", ctime()

def main():
    print "starting at:", ctime()
    thread.start_new_thread(loop0,()) # 创建一个线程,并在其中运行函数loop0
    thread.start_new_thread(loop1,()) # 创建另一个线程,并在其中运行函数loop1
    sleep(6) # 不可缺少,由于主线程并没有停下来,它不会等两个子线程运行完后,再继续运行之后的代码。如果将这一句注释掉,程序只会运行main(),不执行loop0和loop1两个函数
    print "all DONE at:", ctime()

if __name__ == "__main__":
    main()

mtsleep2.py

#-*-coding: utf-8-*-

import thread
from time import sleep, ctime

loops = [4, 2] # 每个loop的sleep时间

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()

mtsleep3.py

#-*-coding: utf-8-*-

import threading
from time import sleep, ctime

loops = [4, 2] # 每个loop的sleep时间

def loop(nloop, nsec):
    print "start loop", nloop, "at:", ctime()
    sleep(nsec)
    print "loop", nloop, "done at:", ctime()

def main():
    print "starting at:", ctime()
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = threading.Thread(target=loop, args=(i, loops[i])) # Thread对象,也是传入一个函数对象和相应的参数作为参数,但在这一步,线程并未开始。
        threads.append(t)

    for i in nloops:
        threads[i].start() # 线程开始

    for i in nloops:
        threads[i].join() # join()函数允许主线程等待子线程运行完毕,无需再管理锁
 
    print "all DONE at:", ctime()

if __name__ == "__main__":
    main()

mtsleep4.py

#-*-coding: utf-8-*-

import threading
from time import sleep, ctime

loops = [4, 2]

class ThreadFunc(object):
    
    def __init__(self, func, args, name=''):
        self.name = name
        self.func = func
        self.args = args

    def __call__(self):
        apply(self.func, self.args)

def loop(nloop, nsec):
    print "start loop", nloop, "at:", ctime()
    sleep(nsec)
    print "loop", nloop, "done at:", ctime()

def main():
    print "starting at:", ctime()
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__)) # Thread对象传入可调用的类对象作为参数,实际是可调用实例作为参数?
        threads.append(t)

    for i in nloops:
        threads[i].start()

    for i in nloops:
        threads[i].join()

    print "all DONE at:", ctime()

if __name__ == "__main__":
    main()

mtsleep5.py

#-*-coding: utf-8-*-

import threading
from time import sleep, ctime

loops = (4, 2)

class MyThread(threading.Thread):    
    def __init__(self, func, args, name=''):
        threading.Thread.__init__(self) # 先调用基类threading.Thread的构造器
        self.name = name
        self.func = func
        self.args = args

    def run(self): # 定义线程的功能,在派生子类中被重写,在本例中就是运行loop()函数。
        apply(self.func, self.args)

def loop(nloop, nsec):
    print "start loop", nloop, "at:", ctime()
    sleep(nsec)
    print "loop", nloop, "done at:", ctime()

def main():
    print "starting at:", ctime()
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = MyThread(loop, (i, loops[i]), loop.__name__)
        threads.append(t)

    for i in nloops:
        threads[i].start()

    for i in nloops:
        threads[i].join()

    print "all DONE at:", ctime()

if __name__ == "__main__":
    main()

myThread.py

#-*-coding: utf-8-*-

import threading
from time import sleep, ctime

loops = (4, 2)

class MyThread(threading.Thread):    
    def __init__(self, func, args, name=''):
        threading.Thread.__init__(self) # 先调用基类threading.Thread的构造器
        self.name = name
        self.func = func
        self.args = args

    def getResult(self):
        return self.res

    def run(self): # 定义线程的功能,在派生子类中被重写,在本例中就是运行loop()函数。
        print "starting", self.name, "at:", ctime()
        self.res = self.func(*self.args)
        print self.name, "finished at:", ctime()

mtfacfib.py

#-*-coding: utf-8-*-

# 先单线程运行fib, fac, sum,再多线程同时运行这三个函数

from myThread import MyThread
from time import ctime, sleep

def fib(x):
    sleep(0.005)
    if x < 2: return 1
    return (fib(x-2) + fib(x-1))

def fac(x):
    sleep(0.1)
    if x < 2: return 1
    return (x * fac(x-1))

def sum(x):
    sleep(0.1)
    if x < 2: return 1
    return (x + sum(x-1))

funcs = [fib, fac, sum]
n = 12

def main():
    nfuncs = range(len(funcs))

    print "*** SINGLE THREAD"
    for i in nfuncs:
        print "starting", funcs[i].__name__, 'at:', ctime()
        print funcs[i](n)
        print funcs[i].__name__, "finished at:", ctime()

    print "\n*** MULTIPLE THREADS"
    threads = []
    for i in nfuncs:
        t = MyThread(funcs[i], (n,), funcs[i].__name__)
        threads.append(t)

    for i in nfuncs:
        threads[i].start()

    for i in nfuncs:
        threads[i].join()
        print threads[i].getResult()

    print "all DONE"

if __name__ == "__main__":
    main()

prodcons.py

#-*-coding: utf-8-*-

# 创建一个队列,让生产者(线程)把新生产的货物放进去供消费者(线程)使用。

from random import randint
from time import sleep
from Queue import Queue
from myThread import MyThread

def writeQ(queue): # 把对象放入队列
    print "producing object for Q..."
    queue.put('xxx', 1)
    print "size now %d" % queue.qsize()

def readQ(queue): # 消耗队列中的一个对象
    val = queue.get(1)
    print "consumed object from Q... size now %d" % queue.qsize()

def writer(queue, loops): # 把对象放入一个队列,等待随机秒,重复loops次
    for i in range(loops):
        writeQ(queue)
        sleep(randint(1, 3)) # writer睡眠时间要短

def reader(queue, loops): # 从队列中消耗一个对象,等待随机秒,重复loops次
    for i in range(loops):
        readQ(queue)
        sleep(randint(2, 5)) # reader睡眠时间要长,尽量保证reader不从空队列中消耗对象

funcs = [writer, reader] # 设置线程数
nfuncs = range(len(funcs))

def main():
    nloops = randint(2, 5)
    q = Queue(32)

    threads = []
    for i in nfuncs:
        t = MyThread(funcs[i], (q, nloops), funcs[i].__name__)
        threads.append(t)

    for i in nfuncs:
        threads[i].start()

    for i in nfuncs:
        threads[i].join()

    print "all DONE"

if __name__ == "__main__":
    main()