线程

1.queue队列

2.进程间通讯IPC机制

3.什么是线程(理论)

4.开启线程的俩种方式

5.生产者消费者模型

6.线程的其他方法

7.线程间是可以通讯的

8.守护线程

9.互斥锁

一.queue队列

 1.队列:先进先出

 2.堆栈:先进后出

 1 # get和put最好是一一对应,而且不要超过你传入的最大值,不会回进入阻塞态
 2 from multiprocessing import Queue
 3 
 4 q = Queue(5)# 括号内可以放参数,表示的是这个队列最大存储量
 5 
 6 q.put('q')
 7 q.put('w')
 8 q.put('e')
 9 q.put('r')
10 q.put('t')
11 q.put('t')# 如果队列被放满了,还继续往队列里面添加值,程序不会报错,会原地等待,直到有人取走值
12 
13 
14 print(q.get())
15 print(q.get())
16 print(q.get())
17 print(q.get())
18 print(q.get())# 如果没有值取空了,在取的话,程序会进入阻塞状态,知道有人继续往队列中添加值
19 
20 
21 '''
22 还有几种方法(但这几种方法都不适用多进程的情况)
23 q.full()判断队列是否满了
24 print(q.empty())判断队列中的数据数据是否取完了
25 print(q.get_nowait())取值,没有值不等待直接报错
26 '''
View Code
 

二.进程间通讯IPC机制

进程间通讯
​通讯指的就是交换数据
进程之间内存是相互隔离的,当一个进程想要把数据给另外一个进程,就需要考虑IPC
用队列可以测验出进程间是可以通过一些方法进行通讯的
from multiprocessing import Process,Queue

def qwe(q):
    q.put('hello ')


def zxc(q):
    print(q.get(q))

if __name__ == '__main__':
    q = Queue()
    p = Process(target=qwe,args=(q,))
    p1 = Process(target=zxc,args=(q,))
    p.start()
    p1.start()


三.什么是线程

什么是线程:
    线程是操作系统最小的运算调度单位,被包含在进程中,一个线程就是一个固定的执行流程

线程和进程的关系:
    线程不能单独存在,必须存在于进程中,
进程是一个资源单位,其中包含了运行程序所需要的所有资源

    线程才是真正的执行单位,没有线程,进程中的资源无法被利用起来,
所以一个进程至少包含一个线程,称之为主线程,当我们启动一个程序时,
操作系统就会自己为这个程序创建一个主线程,线程也可以由程序后期开启,
自己开启的线程称之为子线程


那位什么需要线程:
    开进程:
        1 申请内存空间 ,耗资源
        2 拷贝代码 ,耗资源

    开线程:
        目的只有一个就是提高效率,将内存比如工厂,那么进程就相当于是工厂里面的车间,
        而你的线程就相当于是车间里面的流水线,要是生产量跟不上的话,你肯定是创建新的流水线,
        而不是创建一个新的工厂

    一个进程内可以起多个线程,并且线程与线程之间的数据是共享的
    ps:开启线程的开销要远远小于开启进程的开销


四.开启线程的俩种方式

创建线程的俩种方式
跟创建进程的方式是一样的

一》

from threading import Thread
import time

def task(name):
    print('%s is running'%name)
    time.sleep(2)
    print('%s is over'%name)
# 注意:开启线程的时候不需要在__main__内
t = Thread(target=task,args=('zy',))
t.start()# 告诉操作系统开一个线程,线程的开销远远小于进程
# 小到代码一执行完,线程就开启了
print('主')

二》

from threading import Thread
import time

class MyThread(Thread):
    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print('%s is running'%self.name)
        time.sleep(2)
        print('%s is over'%self.name)

t = MyThread('zy')
t.start()
print('主')

五.生产者消费者模型

生产者消费者是什么:

生产者消费者
生产者消费者模型是什么:
模型,就是解决摸个问题的套路,
产生数据的一方称之为生产者,
处理数据的一方称之为消费者,
列如:爬虫,生活中到处都是这种模型,
饭店 厨师即使生产者,你吃饭的人就是消费者

## 生产者和消费者出啥问题了?   解决什么问题
​    生产者和消费,处理速度不平衡,一方快一方慢,导致一方需要等待另一方
## 生产者消费者模型解决这个问题的思路:     怎么解决
​    原本,双方是耦合 在一起,消费必须等待生产者生成完毕在开始处理, 反过来
​    如果消费消费速度太慢,生产者必须等待其处理完毕才能开始生成下一个数据

### 解决的方案:
​    将双方分开来.一专门负责生成,一方专门负责处理
​    这样一来数据就不能直接交互了 双方需要一个共同的容器
​    生产者完成后放入容器,消费者从容器中取出数据
​    这样就解决了双发能力不平衡的问题,做的快的一方可以继续做,不需要等待另一方

列子:

生产者:生产/制造数据的
消费者:消费/处理数据的
例子:做包子的,买包子的
1.做包子远比买包子的多
2.做包子的远比买包子的少
如何解决供需不平衡的问题

第一种解决方法:
 1 第一个版本
 2 from multiprocessing import Process,Queue,JoinableQueue
 3 import random
 4 import time
 5 
 6 
 7 def zuo(name,food,q):
 8     for i in range(10):
 9         data = '%s 生产了%s%s'%(name,food,i)
10         time.sleep(random.random())
11         q.put(data)
12         print(data)
13 
14 def chi(name,q):
15     while True:
16         data = q.get()
17         if data == None:break# 判断取到的是否为空
18         print('%s吃了%s'%(name,data))
19         time.sleep(random.random())
20 
21 if __name__ == '__main__':
22     q = Queue()
23     z = Process(target=zuo,args=('厨师zy','牛排',q))
24     z1 = Process(target=zuo,args=('厨师xj','鸡排',q))
25     c = Process(target=chi,args=('',q))
26     c1 = Process(target=chi,args=('动物',q))
27     z.start()
28     z1.start()
29     c.start()
30     c1.start()
31     z.join()
32     z1.join()# 没有这个的话,直接取空,消费者就结束了,确保生产者确确实实生产完了
33     q.put(None) # 存进去一个None,只要消费者取值得时候取到空了,就带表生产的都被吃完了,然后结束程序
34     q.put(None)
35 
36 第一种方法的缺陷:没多一个人我就要多写一个None,要是人有成千上万怎么办
View Code

第二种方法:用JoinableQueue方法解决

JoinableQueue里面的方法:

JoinableQueue

可以被join的队列

join是等待任务结束

队列怎么叫结束?

调用task_done一次则表示有一个数据被处理完成了 当task_done次数等于put的次数就意味着任务处理完成了

这也是join的执行时机

该队列可以明确告知数据的使用方,所有数据都已经处理完成

在生产者消费者模型中,解决了消费者不知道何时算是任务结束的问题

具体过程:主进程先等待所有的生产者进程生成完毕,再等队列中的数据被全部处理,这就意味着,任务全部结束

 1 from multiprocessing import Process,JoinableQueue
 2 import random
 3 import time
 4 
 5 
 6 def zuo(name,food,q):
 7     for i in range(10):
 8         data = '%s 生产了%s%s'%(name,food,i)
 9         time.sleep(random.random())
10         q.put(data)
11         print(data)
12 
13 def chi(name,q):
14     while True:
15         data = q.get()
16         print('%s吃了%s'%(name,data))
17         time.sleep(random.random())
18         q.task_done()# 告诉队列你已经从队列中取出了一个数据,并且处理完毕
19 
20 if __name__ == '__main__':
21     q = JoinableQueue()
22     z = Process(target=zuo,args=('厨师zy','牛排',q))
23     z1 = Process(target=zuo,args=('厨师xj','鸡排',q))
24     c = Process(target=chi,args=('',q))
25     c1 = Process(target=chi,args=('动物',q))
26     z.start()
27     z1.start()
28     c.daemon = True# 将消费者设为守护进程,主线程结束,子线程也就结束了
29     c1.daemon = True
30     c.start()
31     c1.start()
32     z.join()# 确保生产者确确实实生产完了
33     z1.join()
34 
35     q.join()# 等待队列中数据全部取出
View Code

六.线程的其他方法

from threading import Thread,current_thread,active_count
import time
import os

def task(name,i):
    print('%s is running'%name)
    # print('子current_thread:',current_thread().name)# 查看子线程
    # print('子',os.getpid())
    time.sleep(i)

    print('%s is over'%name)
# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
t = Thread(target=task,args=('egon',1))
t1 = Thread(target=task,args=('jason',2))
t.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
t1.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
t1.join()  # 主线程等待子线程运行完毕
print('当前正在活跃的线程数',active_count())# 查看正在活跃的线程数
# 小的代码执行完 线程就已经开启了
print('')
# print('主current_thread:',current_thread().name)
# print('主',os.getpid())# 查看线程id

七.线程间是可以通讯的

from threading import Thread

money = 666

def task():
    global money
    money = 999

t = Thread(target=task)
t.start()
t.join()
print(money)

 线程之间是可以通信的,数据之间不是相互隔离的
结果是999

八.守护线程

一个线程可以设置为另一个线程的守护进程
特点:被守护线程结束后守护线程也随之结束

守护线程会等到所有非守护线程结束后结束!前提是除了主线程之外,还有其他的非守护线程
当然如果守护线程已经完成任务,就会立马结束
 1 from threading import Thread
 2 import time
 3 
 4 def task():
 5     print("子1running......")
 6     time.sleep(5)
 7     print("子1over......")
 8 
 9 def task2():
10     print("子2running......")
11     time.sleep(4)
12     print("子2over......")
13 
14 t = Thread(target=task)
15 t.daemon = True
16 t.start()
17 
18 t2 = Thread(target=task2)
19 t2.start()
20 print("主over")
View Code

九.互斥锁

线程互斥锁
不管是进程还是线程,只要共享就意味着竞争,线程中也存在安全问题,
多线程可以并发执行,一旦并发了并且访问了同一个资源就会出现问题,
解决方案:还是要加互斥锁
 1 from threading import Thread,Lock
 2 import time
 3 
 4 n = 100
 5 
 6 def task(mutex):
 7     global  n
 8     mutex.acquire()
 9     tmp = n
10     time.sleep(0.1)
11     n = tmp - 1
12     mutex.release()
13 
14 t_list = []
15 mutex = Lock()
16 for i in range(100):
17     t = Thread(target=task,args=(mutex,))
18     t.start()
19     t_list.append(t)
20 for t in t_list:
21     t.join()
22 print(n)
23 
24 开100个线程,每一次都去减一,最终打印结果为0,如果不加锁的话打印结果为99,因为每个线程拿到的都是100,100减1等于99
View Code
原文地址:https://www.cnblogs.com/zahngyu/p/11342197.html