先来说说什么是线程

建议大家去看看关于操作系统的书籍, 也可以去看一些公开课,记得好像是斯坦福大学有一门公开课好像就是讲 操作系统 的,记不清楚了, 大家可以查一下.

说到线程就要说到 什么是进程

进程是具有一定独立功能的程序关于某个数据集合上的一次运动行动,是系统进行资源分配和调度的一个基本单位.

线程 是进程中的一个实体,是CPU调度和分派的基本单位,线程不独立存在,从属于进程


两者的关系

线程属于进程,线程运行在进程空间,同一进程产生的线程共享都属于该进程的资源, 但是线程本身并不用友系统资源, 只拥有一点在运行中必不可少的信息( 如计数器\寄存器 等)

而进程之间的资源并不共享,进程与进程之间是独立的,

进程拥有一个或多个线程, 即 进程至少拥有一个线程.

进程和线程都可以并发, 相比起来 线程更节省系统开销,

关于线程的模块threading

例:

import threading
():
    threads = []

i ():
    t = threading.Thread(=)
    threads.append(t)
    t.start()


常用方法

start()  启动线程,等待CPU调度
join()  逐个执行每个线程, 该方法是的多线程无意义

run()  CPU调度线程后执行Thread类的 run方法

isAlive()   返回线程是否存活

isDeamon()  判断线程是否是Deamon线程

setDaemon()   设置为后台线程或者前台线程,默认为前台线程

getName()  获取线程名称

setName()   为线程设置名称


注意:

如果线程被设置成后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;  如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止


线程锁

在使用线程的时候,我们需要注意一个问题, 如果同一资源 同时被多个线程处理 ,就无法保证数据的安全,这个时候就要用到线程锁的概念, 即 在一个线程使用一份资源的时候,限制其他线程来访问该资源,等该线程处理完成释放该资源以后,其他的线程才能访问.

例:

import threading
import time
gl_num = 0
lock = threading.RLock()  #这里就是上面提到的锁
   
def Func():
    lock.acquire()
    global gl_num
    gl_num +=1
    time.sleep(1)
    print gl_num
    lock.release()
       
for i in range(10):
    t = threading.Thread(target=Func)
    t.start()


事件

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

  • clear:将“Flag”设置为False

  • set:将“Flag”设置为True

import threading
 
def do(event):
    print 'start'
    event.wait()
    print 'execute'
 
event_obj = threading.Event()
for i in range(10):
    t = threading.Thread(target=do, args=(event_obj,))
    t.start()
 
event_obj.clear()
inp = raw_input('input:')
if inp == 'true':
    event_obj.set()



关于线程这里还会涉及到 SocketServer 模块

主要是下面两个类

SocketServer.ForkingTCPServer
SocketServer.ThreadingTCPServer


ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

python学习笔记-Day10--(进程\线程\协程)_线程

内部调用流程为:

  • 启动服务端程序

  • 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口

  • 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass

  • 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...

  • 当客户端连接到达服务器

  • 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求

  • 执行 ThreadingMixIn.process_request_thread 方法

  • 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass()  即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)



ForkingTCPServer和ThreadingTCPServer的使用和执行流程基本一致,只不过在内部分别为请求者建立 “线程”  和 “进程”。


server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyRequestHandler)

SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 os.fork 两个东西,其实本质上就是在服务器端为每一个客户端创建一个进程,当前新创建的进程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)



###############################3


进程


对于python来说 在多线程上一直被人诟病,这个也是python的内部实现的问题. (这里指Cpython)

python存在一个 GIL (Global Interpreter Lock,全局解释器锁) ,在解释代码的时候,会产生互斥锁 来限制线程对共享资源的访问, 所以虽然python的线程库直接封装了远程线程,但是python中的进程,同一时间只有一个获得GIL的线程在运行,其他的线程则是在等待,这就造成了 在多核CPU中多线程也只是像单线程一样在运行,

so  我们还要了解进程

模块是multiprocessing


进程之间数据的共享

进程与进程之间默认是无法共享数据的,
如果需要共享,我们可以使用下面两种方法

创建进程的时候(还为开始使用),将共享数据拿到子进程中,进程执行完后,在讲处理结果赋值给原来的值

#方法一,Array
from multiprocessing import Process,Array
temp = Array('i', [11,22,33,44])   #这里的 'i' 有特殊含义
 
def Foo(i):
    temp[i] = 100+i
    for item in temp:
        print i,'----->',item
 
for i in range(2):
    p = Process(target=Foo,args=(i,))
    p.start()
 
#方法二:manage.dict()共享数据
from multiprocessing import Process,Manager
 
manage = Manager()
dic = manage.dict()
 
def Foo(i):
    dic[i] = 100+i
    print dic.values()
 
for i in range(2):
    p = Process(target=Foo,args=(i,))
    p.start()
    p.join()


使用Array时,遇到的第一个参数"i",含义如下,

  'c': ctypes.c_char,  'u': ctypes.c_wchar,
    'b': ctypes.c_byte,  'B': ctypes.c_ubyte,
    'h': ctypes.c_short, 'H': ctypes.c_ushort,
    'i': ctypes.c_int,   'I': ctypes.c_uint,
    'l': ctypes.c_long,  'L': ctypes.c_ulong,
    'f': ctypes.c_float, 'd': ctypes.c_double


进程同样会有 竞争资源的情况,所以也会涉及到锁 ,这里叫进程锁.

使用方法与线程类似

import multiprocessing
a= multiprocessing.RLock()
a.acquire()


进程里 还有一个进程池的概念

进程池就是一个进程序列, 使用进程池的时候, 就去进程池中获取一个进程,如果此时进程池中没有进程,那么程序就会等待,直到进程池中有进程,( 有点想队列 )

multiprocessing.Pool()

进程池中的方法

apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,

apply(func[, args[, kwds]])是阻塞的

close()    关闭pool,使其不在接受新的任务。
terminate()    结束工作进程,不在处理未完成的任务。
join()    主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用


#!/usr/bin/env python
# -*- coding:utf-8 -*-
from  multiprocessing import Process,Pool
import time
 
def Foo(i):
    time.sleep(2)
    return i+100
 
def Bar(arg):
    print arg
 
pool = Pool(5)
#print pool.apply(Foo,(1,))
#print pool.apply_async(func =Foo, args=(1,)).get()
 
for i in range(10):
    pool.apply_async(func=Foo, args=(i,),callback=Bar)
 
print 'end'
pool.close()
pool.join()#进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。



最后来说一个东西 ---- 协程

线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

这里需要用到两个模块

greenlet 和 gevent

这两个模块默认没有安装 ,需要安装后才可以使用.

        from greenlet import greenlet
        def test1():
            print 12
            gr2.switch()
            print 34
            gr2.switch()

        def test2():
            print 56
            gr1.switch()
            print 78
         
        gr1 = greenlet(test1)
        gr2 = greenlet(test2)
        gr1.switch()

_________________________________________________________________

import gevent
         
        def foo():
            print('Running in foo')
            gevent.sleep(0)
            print('Explicit context switch to foo again')
         
        def bar():
            print('Explicit context to bar')
            gevent.sleep(0)
            print('Implicit context switch back to bar')
         
        gevent.joinall([
            gevent.spawn(foo),
            gevent.spawn(bar),
        ])


参考

http://www.cnblogs.com/wupeiqi/articles/5040823.html