第二十五篇、进程和线程

一、进程与线程之间的关系

线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

 1 优点:同时利用多个cpu,能够同时进行多个操作 2 缺点:耗费资源(重新开辟内存空间) 

 1 优点:共享内存,IO操作时候,创造并发操作 2  缺点:抢占资源 

进程不是越多越好,cpu个数=进程个数

线程也不是越多越好,具体案例具体分析,请求上下文切换耗时

1 1、计算机执行程序任务的最小单元是线程
2 2、所有的操作都不是利用cpu的,IO操作不是利用cpu的
3 所以
4 IO密集型(不用cpu)
5 用多线程
6 计算密集型(用cpu)
7 用多进程

二、使用线程

1、

    import threading
        def f0():
            pass
        def f1(a1,a2):
            time.sleep(10)
            f0()
        t1 = threading.Thread(target=f1,args=(123,111,))
        t1.start()
        t1.join()

        t = threading.Thread(target=f1,args=(123,111,))
        t.setDaemon(True)
        t.start()
        t = threading.Thread(target=f1,args=(123,111,))
        t.setDaemon(True)
        t.start()
线程代码

以上创建的线程叫做3个子线程

上面三个线程执行完毕之后就夯住了,然后等待10秒结束

2、

上面的setDeamon如果为True的时候就可以设置主线程是否为后台线程。如果是,那么主线程执行完毕,就不会等待子线程是否执行完毕,直接结束

主线程等待,子线程执行

3、

t.join():这个线程执行完毕之后再往下执行,如果里面有参数n,表示这个线程最多等待n秒就往下执行,如果n大于主线程时间a,那么就最多等待a秒

 1 t.start() : 激活线程,
 2 
 3 t.getName() : 获取线程的名称
 4 
 5 t.setName() : 设置线程的名称 
 6 
 7 t.name : 获取或设置线程的名称
 8 
 9 t.is_alive() : 判断线程是否为激活状态
10 
11 t.isAlive() :判断线程是否为激活状态
12 
13 t.setDaemon() 设置为后台线程或前台线程(默认:False);通过一个布尔值设置线程是否为守护线程,必须在执行start()方法之后才可以使用。如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
14 
15 t.isDaemon() : 判断是否为守护线程
16 
17 t.ident :获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才有效,否则它只返回None。
18 
19 t.join() :逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
20 
21 t.run() :线程被cpu调度后自动执行线程对象的run方法
更多方法

三、线程锁threading.RLock和threading.Lock

我们使用线程对数据进行操作的时候,如果多个线程同时修改某个数据,可能会出现不可预料的结果,为了保证数据的准确性,引入了锁的概念

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import threading
import time
glo_num = 0

lock = threading.RLock()

def func():
    lock.acquire() #获的锁
    global glo_num
    glo_num +=1
    time.sleep(1)
    print(glo_num)
    lock.release()#释放锁

for i in range(10):
    t = threading.Thread(target=func)
    t.start()

结果
1
2
3
4
5
6
7
8
9
10
代码

threading.RLock和threading.Lock 的区别

RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。 如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。

所以

lock不好,建议用Rlock

四、线程事件threading.Event

Event是线程间通信最间的机制之一:一个线程发送一个event信号,其他的线程则等待这个信号。用于主线程控制其他线程的执行。 Events 管理一个flag,这个flag可以使用set()设置成True或者使用clear()重置为False,wait()则用于阻塞,在flag为True之前。flag默认为False。

#!/usr/bin/env python
#-*- coding:utf-8 -*-
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 = input("input:")
if inp == "True":
    event_obj.set()  #让等边绿

当用户输入true的时候,才能让车路过
红绿灯例子代码
Event.wait([timeout]) : 堵塞线程,直到Event对象内部标识位被设为True或超时(如果提供了参数timeout)。
Event.set() :将标识位设为Ture
Event.clear() : 将标识伴设为False。
Event.isSet() :判断标识位是否为Ture。
方法

五、队列和生产者消费者模式

1 Queue 就是对队列,它是线程安全的
2 举例来说,我们去肯德基吃饭。厨房是给我们做饭的地方,前台负责把厨房做好的饭卖给顾客,顾客则去前台领取做好的饭。这里的前台就相当于我们的队列。(先进先出)
3 这个模型也叫生产者-消费者模型。
4 python2中的queue是大写开头,3里面是小写
#!/usr/bin/env python
import Queue
import threading


message = Queue.Queue(10)


def producer(i):
    print(“aa”)
#while True:
        message.put(i)


def consumer(i):
    print(“aa”)
    #while True:
        msg = message.get()


for i in range(12):
    t = threading.Thread(target=producer, args=(i,))
    t.start()

for i in range(10):
    t = threading.Thread(target=consumer, args=(i,))
    t.start()
代码例子
import queue

q = queue.Queue(maxsize=0)  # 构造一个先进显出队列,maxsize指定队列长度,为0 时,表示队列长度无限制。

q.join()    # 等到队列为kong的时候,在执行别的操作
q.qsize()   # 返回队列的大小 (不可靠)
q.empty()   # 当队列为空的时候,返回True 否则返回False (不可靠)
q.full()    # 当队列满的时候,返回True,否则返回False (不可靠)
q.put(item, block=True, timeout=None) #  将item放入Queue尾部,item必须存在,可以参数block默认为True,表示当队列满时,会等待队列给出可用位置,
                         为False时为非阻塞,此时如果队列已满,会引发queue.Full 异常。 可选参数timeout,表示 会阻塞设置的时间,过后,
                          如果队列无法给出放入item的位置,则引发 queue.Full 异常
q.get(block=True, timeout=None) #   移除并返回队列头部的一个值,可选参数block默认为True,表示获取值的时候,如果队列为空,则阻塞,为False时,不阻塞,
                      若此时队列为空,则引发 queue.Empty异常。 可选参数timeout,表示会阻塞设置的时候,过后,如果队列为空,则引发Empty异常。
q.put_nowait(item) #   等效于 put(item,block=False)
q.get_nowait() #    等效于 get(item,block=False)
方法

方法中的put(往队列里面放值)

get方法(从队列里面取值)

队列的例子:12306网站就是内存级别的队列

还有更好的队列,后面会说到

六、multiprocessing模块

multiprocessing是python的多进程管理包,和threading.Thread类似。直接从侧面用subprocesses替换线程使用GIL的方式,由于这一点,multiprocessing模块可以让程序员在给定的机器上充分的利用CPU。

在multiprocessing中,通过创建Process对象生成进程,然后调用它的start()方法

from multiprocessing import Process
 
def f(name):
    print('hello', name)
 
if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
代码

七、进程

import multiprocessing
import time
def f1(a1):
    time.sleep(2)
    print(a1)
if __name__ == “__main__”:
t = multiprocessing.Process(target=f1,args=(11,))
t.deamon = True
t.start()
print (“end”)
上面的是两个子进程
1、创建进程

创建进程windows里面运行不了,但是把进程写在if __name__ == “__main__”:里面,但是总不可能永远写在这里面,所以要写在linux中运行

2、daemon和线程的一样,是否为后台进程True 为后台进程,即主线程执行完毕之后就不再执行其他

3、join和线程类似,join这个进程执行完毕之后才往下执行

4、进程和进程之间的数据是不能够共享的,如果一个程序能够随便进入另一个进程里面读取数据会造成混乱   如某某管家

#!/usr/bin/env python
#-*- coding:utf-8 -*-
from multiprocessing import Process
import threading
import time
li = []
def foo(i):
    li.append(i)
    print("say hi",li)
if __name__ == "__main__":
    for i in range(10):
        p = Process(target=foo,args=(i,))
        p.start()
结果:
say hi [0]
say hi [1]
say hi [2]
say hi [3]
say hi [4]
say hi [5]
say hi [6]
say hi [7]
say hi [9]
say hi [8]

上面一个进程创建一个新的列表
实例代码
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from multiprocessing import Process
import threading
import time
li = []
def foo(i):
    li.append(i)
    print("say hi",li)
if __name__ == "__main__":
    for i in range(10):
        p = threading.Thread(target=foo,args=(i,))
        p.start()
say hi [0]
say hi [0, 1]
say hi [0, 1, 2]
say hi [0, 1, 2, 3]
say hi [0, 1, 2, 3, 4]
say hi [0, 1, 2, 3, 4, 5]
say hi [0, 1, 2, 3, 4, 5, 6]
say hi [0, 1, 2, 3, 4, 5, 6, 7]
say hi [0, 1, 2, 3, 4, 5, 6, 7, 8]
say hi [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
这个是一个进程里面例子

5、一个程序之间的数据也能进行共享

需要用到如下方法

1、Array方法

数组在定义的时候必须要制定长度。或者元素
数组里面必须是统一的数据类型
from multiprocessing import Process,Array
tmp = Array(“i”,[11,22])  里面的参数I  指定数据类型
关于数组的知识
from multiprocessing import Process,Array
temp = Array('i', [11,22,33,44])
 
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()
代码

2、manage.dict()共享数据

m = Manager()  m.dict()

from multiprocessing import Process,Manager
def Foo(i,dic):
    dic[i] = 100+i
    print(len(dic))

if __name__ == '__main__':
    manage = Manager()
    dic = manage.dict()
    for i in range(2):
        p = Process(target=Foo,args=(i,dic,))
        p.start()
        p.join()
代码
'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
原文地址:https://www.cnblogs.com/pyrene/p/6471553.html