第九天 线程 进程 协程 队列

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第九天 线程 进程 协程 队列相关的知识,希望对你有一定的参考价值。

详细链接http://www.cnblogs.com/alex3714/articles/5230609.html

1.线程:包含在进程中,是操作系统运算调度的最小单位,是一串指令的集合,直接与cpu交互

2进程:进程是一个程序各种资源的集合。操作系统通过管理这个集合进而运行程序,进程本身并不执行,进程通过调用线程来调度cpu。

3.不同点:

一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程

创建新线程很简单,但是创建一个子进程需要对父进程进行拷贝

线程共享内存,进程的内存是独立的

线程间可以直接交流数据,进程之间不能直接交流数据

4.多线程示例:

 1 import threading
 2 import time
 3 def run(n,m):
 4     print("task",n,m)
 5 t1 = threading.Thread(target=run,args=(t1,f,)) #args是函数参数的集合,注意逗号
 7 t2 = threading.Thread(target=run,args=(t2,g,))
 8 t1.start()
 9 t2.start()
10 
11 输出:
12 task t1 f
13 task t2 g

5.计算线程总共花费的时间:

threading.Thread.join()  等待start的线程运行结束
 1 import threading
 2 import time
 3 t_list = []
 4 start_time = time.time()
 5 def run():
 6     print("task")
 7     time.sleep(3)
 8     print("ok")
 9 for i in range(50):
10     t = threading.Thread(target=run)
11     t.start()
12     t_list.append(t)
13 cc = []
14 print(t_list)
15 for t in t_list:
16     t.join()
17     c = time.time()-start_time
18     cc.append(c)
19 print(cc)
20 cost = time.time()-start_time
21 print("cost",cost)
22 
23 
24 输出:cost 3.009999990463257

6.将线程设置为守护线程,主进程执行完后程序就结束退出了,不会等守护线程执行结束

一、设置守护进程

import threading
import time
t_list = []
start_time = time.time()
def run():
    print("task")
    time.sleep(3)
    print("ok")
for i in range(50):
    t = threading.Thread(target=run)
    t.setDaemon(True)  #将线程设置为守护线程
    t.start()
    t_list.append(t)
cost = time.time()-start_time
print("cost",cost)

输出:cost 0.0070040225982666016

二、不设置守护线程

import threading
import time
t_list = []
start_time = time.time()
def run():
    print("task")
    time.sleep(3)
    print("ok")
for i in range(50):
    t = threading.Thread(target=run)
    ##t.setDaemon(True)  #未将线程设置为守护线程
    t.start()
    t_list.append(t)
for t in t_list:       去 ‘’
    t.join()
cost = time.time()-start_time
print("cost",cost)


输出:cost 3.0090019702911377

7.在python2中,有时候线程会重复执行相同的操作,比如:从1加,一直,加到结果等于1000,可能会出现结果等于987或其他结果, 这时要使多线程变成串行。

线程锁(互斥锁Mutex)

例子:

 1 import threading
 2 import time
 3 t_list = []
 4 start_time = time.time()
 5 lock = threading.Lock()  #先实例化一个锁
 6 num = 0
 7 def run():   #只需在这个函数内加锁即可
 8     lock.acquire()  #获取一把锁
 9     print("task")
10     global num
11     num = num +1
12     time.sleep(1)
13     print("ok")
14     print(num)
15     lock.release()  #将锁释放
16 for i in range(50):
17     t = threading.Thread(target=run)
18     t.setDaemon(True)
19     t.start()
20     t_list.append(t)
21 
22 cc = []
23 for t in t_list:
24     t.join()
25     c = time.time()-start_time
26     cc.append(c)
27 print(cc)
28 cost = time.time()-start_time
29 print("cost",cost)
30 
31 
32 
33 输出:结果符合预期

8.threading.active_count() #获取当前活动线程的数量,返回一个数值

9.信号量:  

sem = threading.BoundedSemaphore(5)#实例化信号量,指定同一时间只运行5个线程,当有一个线程运行完后,增加一个新的线程运行。
 1 import threading
 2 import time
 3 sem = threading.BoundedSemaphore(5)
 4 t_list = []
 5 start_time = time.time()
 6 def run():
 7    # lock.acquire()
 8     sem.acquire()
 9     print("task")
10     time.sleep(1)
11     print("""
12    ******************
13    """)
14     print("ok")
15     sem.release()
16 for i in range(20):
17     t = threading.Thread(target=run)
18     t.start()
19     t_list.append(t)
20 cost = time.time()-start_time
21 print("cost",cost)
22 
23 输出:task
24 task
25 task
26 task
27 task
28 cost 0.0040018558502197266
29 
30    ******************
31    
32    ******************
33    
34    ******************
35    
36 ok
37 
38 
39 ok
40 ok
41 task
42 task
43 task

10.线程标记位  event.set()   event.clear()  event.is_set()

import threading
import time
event = threading.Event()
def lighter():
    count = 0
    event.set()#设置标记位
    while True:
        if count > 5 and count < 10:
            event.clear()#清除标记位
            print("\\033[41;1m红灯\\033[0m")
        elif count > 10:
            event.set()#设置标记位
            count = 0#count清0,重新累加
        else:
            print("\\033[42;1m绿灯\\033[0m")
        time.sleep(1)
        count += 1
def car(name):
    while True:
        if event.is_set():#判断是否是标记位,代表绿灯
            print("%s开车"%name)
            time.sleep(1)
        else:
            print("%s停车"%name)
            event.wait()#等待设置新的标记位,当新的标记位产生后,跳出else

light = threading.Thread(target=lighter,)
light.start()
car1 = threading.Thread(target=car,args=(Tesla,))
car1.start()


输出:绿灯
Tesla开车
Tesla开车
绿灯
绿灯
Tesla开车
绿灯
Tesla开车
绿灯
Tesla开车
Tesla开车
绿灯
红灯
Tesla停车
红灯
红灯
红灯
绿灯
Tesla开车

11.队列

queue.Queue(maxsize=0) #先入先出

queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列

queue.LifoQueue(maxsize=0) #后入先出

创建一个“队列”对象
import Queue
q = Queue.Queue(maxsize = 10)
Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。

将一个值放入队列中
q.put(10)
调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。

将一个值从队列中取出
q.get()
调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。

Python Queue模块有三种队列及构造函数:
1、Python Queue模块的FIFO队列先进先出。 class Queue.Queue(maxsize)
2、LIFO类似于堆,即先进后出。 class Queue.LifoQueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。 class Queue.PriorityQueue(maxsize)

此包中的常用方法(q = Queue.Queue()):
q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)
非阻塞 q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作

一些需要注意的地方:

1. 阻塞模式

import Queue

q = Queue.Queue(10)

......
       for i in range(10):
               q.put(‘A‘)
               time.sleep(0.5)

这是一段极其简单的代码(另有两个线程也在操作队列q),我期望每隔0.5秒写一个‘A‘到队列中,但总是不能如愿:间隔时间有时会远远超过0.5秒。原来,Queue.put()默认有 block = True 和 timeou 两个参数。当  block = True 时,写入是阻塞式的,阻塞时间由 timeou  确定。当队列q被(其他线程)写满后,这段代码就会阻塞,直至其他线程取走数据。Queue.put()方法加上 block=False 的参数,即可解决这个隐蔽的问题。但要注意,非阻塞方式写队列,当队列满时会抛出 exception Queue.Full 的异常。

2. 无法捕获 exception Queue.Empty 的异常

while True:
                ......
                try:
                        data = q.get()
                except Queue.Empty:
                        break

我的本意是用队列为空时,退出循环,但实际运行起来,却陷入了死循环。这个问题和上面有点类似:Queue.get()默认的也是阻塞方式读取数据,队列为空时,不会抛出 except Queue.Empty ,而是进入阻塞直至超时。 加上block=False 的参数,问题迎刃而解。

 

以上是关于第九天 线程 进程 协程 队列的主要内容,如果未能解决你的问题,请参考以下文章

Python之路第一课Day9--随堂笔记之二(进程线程协程篇)

线程进程和协程

深入理解进程,线程,协程

python教程16线程,进程,协程,queue队列

测开之并发编程篇・《并发并行线程队列进程和协程》

初识进程 线程 协程:协程