进程.线程

大城市里の小女人 提交于 2019-12-23 07:10:24

目录:

    进程

      子进程

      僵尸孤儿

      进程隔离

      属性方法

      守护进程

      互斥锁

      进程池

      进程通信

      抢票

       生产者消费者

    线程

      守护线程

      常用方法      

      互斥锁

      死锁

      递归锁

      线程池

      线程通讯:event

      信号量

进程与线程配图:

为什么说多线程适合IO密集型任务,而多进程适合计算密集型任务?
    IO密集型主要是要在遇到IO时候去尽快的进行切换,而多线程的切换速度快;
    计算密集型主要是需要使用CPU的资源,而进程是运行的程序,主要是在使用CPU;

 

线程=》单指代码的执行过程进程-》资源的申请与销毁的过程

 

进程:

进程指的就是一个正在运行的程序,或者说是程序的运行过程,即进程是一个抽象的概念.进程之间数据不共享.
进程是起源于操作系统的,是操作系统最核心的概念,操作系统所有其他的概念都是围绕进程展开的
串行:一个任务完完整运行完毕,才执行下一个
并发:多个任务看起来是同时运行的,单核就可以实现并发.本质是不停切换,随机进行
并行:多个任务是真正意义上的同时运行,只有多核才能实现并行 阻塞: 遇到I/O操作,CPU转让给其他进程
 1 一个程序只能有一个进程吗?
 2     可以 启动一次就产生一个进程  当然可以用代码控制是否允许多进程
 3 
 4 启动进程的方式
 5     1.系统初始化,会产生一个根进程
 6     2.用户的交互请求,鼠标双击某个程序
 7     3.在一个进程中,发起了系统调用启动了另一个进程 ******
 8     4.批处理作业开始,某些专用计算机可能还在使用
 9 
10 不同操作系统创建进程的方式不同
11     unix <   centos mac  linux
12         完全拷贝父进程的所有数据,子进程可以访问父进程的数据吗?不可以,但是可以访问拷贝过来数据副本
13     windows
14         创建子进程,导入模块,加载父进程中所有可执行的文件 

开启子进程的两种方式:

我们的代码只负责通知操作系统创建进程,创建完就继续其他代码但是操作系统什么时候创建完成,什么时候执行我们无法预知无法控制
 1 def task(i):
 2     print("%s start" % i)
 3     time.sleep(2)
 4     print("%s stop" % i)
 5 
 6 if __name__ == '__main__':
 7     start_time = time.time()
 8     ps = []
 9     for i in range(1,4):
10         p = Process(target=task,args=(i,))
11         p.start()
12         ps.append(p)
13         # 主进程等子进程结束
14     for p in ps:
15         p.join()
16 
17     print("主进程!!!!",time.time()-start_time)
18 # 3 start   进程产生的指令发给了操作系统,但是具体什么时候产生还需要操作系统来决定
19 # 1 start
20 # 2 start
21 # 3 stop
22 # 1 stop
23 # 2 stop
24 # 主进程!!!! 2.134999990463257

方式一:

 1 # 方式一(常用):
 2 from multiprocessing import Process
 3 import time
 4 
 5 def task(x):#子进程的执行代码
 6     print('%s is running' %x)
 7     print('%s is done' %x)
 8 
 9 if __name__ == '__main__':#兼容window和Linux系统,window系统开启子进程是通过导入的方式拿到父进程数据,Linux是通过直接拷贝拿到
10     # Process(target=task,kwargs={'x':'子进程'})
11     p=Process(target=task,args=('子进程',)) # 如果args=(),是元组,括号内只有一个参数,一定记住加逗号
12     p.start() # 只是在向操作系统发送一个开启子进程的信号
13     time.sleep(2)#加上睡眠时间,使子进程有足够的时间开启
14     print('主')
15 #子进程 is running
16 #子进程 is done
17 #主
18 如果去掉time.sleep(2),执行结果不同
19 #主
20 #子进程 is running
21 #子进程 is done

 方式二:

 1 # 方式二:自定义子进程
 2 from multiprocessing import Process
 3 import time
 4 
 5 class Myprocess(Process):
 6     def __init__(self,x):
 7         super().__init__()
 8         self.name=x
 9 
10     def run(self):#函数名不能改,start方法自动调用run()方法
11         print('%s is running' %self.name)
12         time.sleep(3)
13         print('%s is done' %self.name)
14 
15 if __name__ == '__main__':
16     p=Myprocess('子进程1')
17     p.start()  #p.run()
18     print('主')
19 #主
20 #子进程1 is running
21 #子进程1 is done

僵尸和孤儿进程:

僵尸进程:  一个子进程任务执行完就死亡了,但是操作系统不会立即将其清理,为的是让父进程可以访问到这个子进程的运行信息(执行时间,PID等)
子进程死亡,父进程还没结束,没有被操作系统清理的进程称为僵尸进程,越少越好
如果父进程结束了,死亡的子进程会被清理掉.但是如果父进程不结束,且子进程一直循环产生,就会导致僵尸进程越来越多,占用内存和PID
孤儿进程:
  孤儿进程是无害!没有爹的称为孤儿.一个父进程已经死亡然而他的子孙进程还在执行着,这时候操作系统会接管这些孤儿进程,最终将其清理

 进程内存隔离:join方法

 1 from multiprocessing import Process
 2 import time
 3 
 4 # 证明进程之间的内存空间彼此隔离
 5 x = 100
 6 
 7 def task():
 8     global x
 9     x = 0#将子进程内存空间中的x改为了0
10     print('done')
11 
12 if __name__ == '__main__':
13     p = Process(target=task)
14     p.start()
15     time.sleep(5) # 让父进程在原地等待,等5s,如果先打印了done,说明先执行了子进程,然后才执行睡眠后的下一行代码
16     print(x)

进程对象的属性和方法:

 join:让父进程在原地等待,等到子进程运行完毕后,才执行下一行代码

代码:
 1 from multiprocessing import Process
 2 import time
 3 
 4 def task(name):
 5     print('%s is running ' %name)
 6     time.sleep(3)
 7     print('%s is done ' % name)
 8 
 9 if __name__ == '__main__':
10     p=Process(target=task,args=('子进程1',))
11     p.start()
12     p.join() # 让父进程在原地等待,等到子进程运行完毕后,才执行下一行代码
13     print('主')
并发执行代码:
 1 from multiprocessing import Process
 2 import time
 3 
 4 def task(name,n):
 5     print('%s is running ' %name)
 6     time.sleep(n)
 7     print('%s is done ' % name)
 8 
 9 if __name__ == '__main__':
10     # p1=Process(target=task,args=('子进程1',1))
11     # p1.start()
12     # p2=Process(target=task,args=('子进程2',2))
13     # p2.start()
14     # p3=Process(target=task,args=('子进程3',3))
15     # p3.start()
16 
17     p_l=[]
18     start=time.time()
19     for i in range(1,4):
20         p=Process(target=task,args=('子进程%s' %i,i))
21         p_l.append(p)
22         p.start()
23 
24     # print(p_l)
25     for p in p_l:
26         p.join()
27 
28     stop=time.time()
29 
30     print('主',(stop-start)) 打印结果:

子进程1 is running
子进程3 is running
子进程2 is running
子进程1 is done
子进程2 is done
子进程3 is done
3.1440000534057617

pid:

父进程获得子进程的pid-->p1.pid

 1 from multiprocessing import Process
 2 import time
 3 import os
 4 
 5 def task(n):
 6     print('%s is running ' %os.getpid())
 7     time.sleep(n)
 8     print('%s is done ' % os.getpid())
 9 
10 
11 if __name__ == '__main__':
12     p1=Process(target=task,args=(10,))
13     # print(p1.pid)
14     p1.start()
15     print(p1.pid) # 父进程内查看子pid的方式
16     print('主')

进程获得进程自己的pid-->os.getpid(),获得父进程pid-->os.getppid

 1 from multiprocessing import Process
 2 import time
 3 import os
 4 
 5 def task():
 6     print('自己的id:%s 父进程的id:%s ' %(os.getpid(),os.getppid()))
 7     time.sleep(200)
 8 
 9 if __name__ == '__main__':
10     p1=Process(target=task)
11     p1.start()
12     print('主',os.getpid(),os.getppid())
13     # 爹=》主--》儿子

了解方法:

取进程名字:
 1 from multiprocessing import Process,current_process
 2 import time
 3 
 4 def task():
 5     print('子进程[%s]运行。。。。' %current_process().name)#在当前进程中拿到进程自己的名字
 6     time.sleep(200)
 7 
 8 if __name__ == '__main__':
 9     p1=Process(target=task,name='子进程1')
10     p1.start()
11     print(p1.name)#在主进程中取子进程的名字
12     print(current_process().name)
13     print('主')
结束进程,判断进程状态:
 1 from multiprocessing import Process,current_process
 2 import time
 3 
 4 def task():
 5     print('子进程[%s]运行。。。。' %current_process().name)
 6     time.sleep(2)
 7 
 8 if __name__ == '__main__':
 9     p1=Process(target=task,name='子进程1')
10     p1.start()
11 
12     print(p1.is_alive())#True
13     p1.join()
14     print(p1.is_alive())#False
15 
16     p1.terminate()
17     time.sleep(1)#休眠让子进程运行
18     print(p1.is_alive())#False
19     print('主')

 守护进程:

# 守护进程的代码非常简单
"""
p.daemon = True   
注意必须在启动进程之前执行
  
什么时候需要使用守护进程
例如:qq中有个下载视频,应该用子进程去做.但是下载的过程中qq退出,那么下载也没必要继续了   

"""
from multiprocessing  import Process
import time

def task():
    print("妃子 升级为皇后")
    time.sleep(3)
    print("皇后 挂了")

if __name__ == '__main__':
    p = Process(target=task)
    # 将这个子进程设置为当前进程守护进程
    p.daemon = True
    p.start()
    print("崇祯登基")
    print("崇祯驾崩了....")

进程互斥锁:

概念及注意事项:

什么时候用锁?
  当多个进程,同时读写同一份数据,数据很可能就被搞坏了
  第一个进程写了一个中文字符的一个字节,cpu被切到另一个进程,另一个进程也写了一个中文字符的一个字节,最后文件解码失败
  问题之所以出现,是因为并发无法控住顺序,目前可以使用join来将所有进程并发改为串行(会降低效率)

与join的区别?
  多个进程并发的访问了同一个资源,将导致资源竞争(同时读取不会产生问题,同时修改才会出问题)
  第一个方案:加上join,但是这样就导致了不公平,相当于上厕所得按照颜值来
  第二个方案:加锁,谁先抢到资源谁先处理
相同点: 都变成了串行
不同点:
  1.join顺序固定,锁顺序不固定!
  2.join使整个进程的任务全部串行,而锁可以指定哪些代码要串行

锁使是什么?
  锁本质上就是一个bool类型的标识符,大家(多个进程)在执行任务之前先判断标识符
  互斥锁:两个进程相互排斥

  注意:要想锁住资源必须保证,大家拿到锁是同一把

怎么使用?
  在需要加锁的地方 lock.acquire() 表示锁定
  在代码执行完后 一定要lock.release() 表示释放锁
代码:
lock.acquire()
放需要竞争资源的代码 (同时写入数据)
lock.release()

抢票程序:

 1 """
 2     抢票!
 3     多个用户在同时读写同一个数据
 4    要通过互斥锁保证数据安全,避免多人购买了同一张票
 5 """
 6 from multiprocessing import Process, Lock
 7 import json, time, random
 8 
 9 # 查看余票
10 def show_ticket(name):
11     time.sleep(random.randint(1, 3))
12     with open(r"ticket.json", "rt", encoding="utf-8") as f:
13         dic = json.load(f)
14         print("%s查看 剩余票数:%s" % (name, dic["count"]))
15 
16 # 购买票
17 def buy_ticket(name):
18     # 购买前再次查询
19     with open(r"ticket.json", "rt", encoding="utf-8") as f:
20         # 修改数据
21         dic = json.load(f)
22         if dic["count"] > 0:
23             dic["count"] -= 1
24             # 模拟网络延迟
25             time.sleep(random.randint(1, 3))
26             # 模拟服务器收到数据 写入文件
27             with open(r"ticket.json", "wt", encoding="utf-8") as f2:
28                 json.dump(dic, f2)
29                 print("%s 购票成功!" % name)
30 
31 def task(lock, name):
32     # 查看余票可以并发执行 不需要锁住
33     show_ticket(name)
34     # 购票的操作需要锁 因为设涉及到同时修改数据
35     lock.acquire()
36     buy_ticket(name)
37     lock.release()
38 
39 if __name__ == '__main__':
40     # 买个锁
41     mutex = Lock()
42     for i in range(11):
43         p = Process(target=task, args=(mutex, "客户%s" % i))
44         p.start()
45 
46 # 查询票这个事情,可以多个进程同时执行
47 # 但是买票这个过程,不能同时进行,前一个没买完,后一个就不能买

进程间通讯:

IPC:通过共享内存的方式共享数据(进程间相互隔离)

之所以开启子进程,肯定需要它帮我们完成任务.很多情况下,需要将数据返回给父进程.然而进程内存是物理隔离的
进程间通讯的解决方案:
1.将共享数据放到文件中  速度慢
2.管道PIPE subprocess中的那个管道只能单向通讯,必须存在父子关系
3.共享一块内存区域,让操作系统帮你分配  速度快

第一种:Manager:字典或列表,需要自己处理资源竞争

from multiprocessing import  Process,Manager
import time

def task(dic):
    print("子进程xxxxx")
    # li[0] = 1
    # print(li[0])
    dic["name"] = "xx"

if __name__ == '__main__':
    m = Manager()
    # li = m.list([100])
    dic = m.dict({})
    # 开启子进程 
    p = Process(target=task,args=(dic,))
    p.start()#发送开子进程信号
    time.sleep(3)#让子进程执行
    print(dic)

 第二种:队列Queue(区别于queue):先进先出,后进后出,类似扶梯,不用自己处理资源竞争

from multiprocessing import Process, Queue

# 基础操作 必须要掌握
# 创建一个队列
q = Queue()
# 存入数据
q.put("hello")
q.put(["1","2","3"])
q.put(1)
# 取出数据
print(q.get())
print(q.get())
print(q.get())
print(q.get())

# 阻塞操作 必须掌握
q = Queue(3)
# 存入数据
q.put("hello",block=False)
q.put(["1","2","3"],block=False)
q.put(1,block=False)
# 当容量满的时候 再执行put 默认会阻塞直到执行力了get为止
# 如果修改block=False 直接报错 因为没地方放了
# q.put({},block=False)

# 取出数据
print(q.get(block=False))
print(q.get(block=False))
print(q.get(block=False))
# 对于get   当队列中中没有数据时默认是阻塞的  直达执行了put
# 如果修改block=False 直接报错 因为没数据可取了
print(q.get(block=False))了解内容:
q = Queue(3)
q.put("q", timeout=3)
q.put("q2", timeout=3)
q.put("q3", timeout=3)
# 如果满了 愿意等3秒  如果3秒后还存不进去 就炸
# q.put("q4",timeout=3)

print(q.get(timeout=3))
print(q.get(timeout=3))
print(q.get(timeout=3))
# 如果没了 愿意等3秒  如果3秒后还取不到数据 就炸
print(q.get(timeout=3))

 生产者消费者模型:

模型,设计模式,三层结构等等表示的都是一种编程套路
生产者指的是能够产生数据的一类任务
消费者指的是处理数据的一类任务

需求: 文件夹里有十个文本文档,要求你找出文件中包含习大大关键字的文件
打开并读取文件数据就是生产者
查找关键字的过程就是消费者

生产者消费者模型为什么出现?
生产者的处理能力与消费者的处理能力不匹配不平衡,导致了一方等待另一方,浪费时间
目前我们通过多进程将生产 和 消费分开处理,然后将生产者生产的数据通过队列交给消费者

在生产者消费者模型中,不仅需要生产者消费者,还需要一个共享数据区域,用来存放多产的数据
1.将生产方和消费方耦合度降低
2.平衡双方的能力,提高整体效率

代码实现 :
搞两个进程,一个负责生产,一个负责消费
数据需要共享所以来个队列

代码:

 1 import time, random
 2 from multiprocessing import Process, JoinableQueue
 3 
 4 # 当生产者存入队列的数据等于消费者从队列中拿数据的次数,说明结束了
 5 
 6 # 制作热狗
 7 def make_hotdog(queue, name):
 8     for i in range(3):
 9         time.sleep(random.randint(1, 2))
10         print("%s 制作了一个热狗 %s" % (name, i))
11         # 生产得到的数据
12         data = "%s生产的热狗%s" % (name, i)
13         # 存到队列中
14         queue.put(data)
15         # 装入一个特别的数据 告诉消费方 没有了
16     
17 
18 # 吃热狗
19 def eat_hotdog(queue, name):
20     while True:
21         data = queue.get()
22         time.sleep(random.randint(1, 2))
23         print("%s 吃了%s" % (name, data))
24         # 该函数就是用来记录一共给消费方多少数据了 就是get次数
25         queue.task_done()
26 
27 
28 if __name__ == '__main__':
29     # 创建可以等待的队列
30     q = JoinableQueue()
31     # 生产者
32     p1 = Process(target=make_hotdog, args=(q, "邵钻钻的热狗店"))
33     p2 = Process(target=make_hotdog, args=(q, "egon的热狗店"))
34     p3 = Process(target=make_hotdog, args=(q, "老王的热狗店"))
35     # 消费者
36     c1 = Process(target=eat_hotdog, args=(q, "思聪"))
37     c2 = Process(target=eat_hotdog, args=(q, "李哲"))
38 
39     p1.start()
40     p2.start()
41     p3.start()
42 
43     # 将消费者作为主进程的守护进程
44     c1.daemon = True
45     c2.daemon = True
46 
47     c1.start()
48     c2.start()
49 
50     # 让主进程等三家店全都做完后....
51     p1.join()
52     p2.join()
53     p3.join()
54 
55     # 如何知道生产方生产完了 并且 消费方也吃完了
56 
57     # 方法一:等待做有店都做完热狗在放None
58     # # 添加结束标志   注意这种方法有几个消费者就加几个None 不太合适 不清楚将来有多少消费者
59     # q.put(None)
60     # q.put(None)
61 
62     # 方法二:主进程等到队列结束时再继续  那队列什么时候算结束? 生产者已经生产完了 并且消费者把数据全取完了
63     
64     # 队列等待没有数据再放入
65     q.join()  # 已经明确生产者一共有多少数据
66 
67     # 现在 需要知道什么时候做完热狗了 生产者不知道  消费者也不知道
68     # 只有队列知道
69 
70     print("主进程over")
71     # 生产方不生产了 然而消费方不知道 所以一直等待  get函数阻塞
72     # 三家店都放了一个空表示没热狗了  但是消费者只有两个 他们只要看见None 就认为没有了
73     # 于是进程也就结束了  造成一些数据没有被处理
案例:生产和吃热狗
=================================================

线程:

进程其实不是一个执行单位,进程是一个资源单位
进程必须等待其内部所有线程都运行完毕才结束
每个进程内自带一个线程,线程是cpu上的执行单位如果把操作系统比喻为一座工厂    在工厂内每造出一个车间===》启动一个进程    每个车间内至少有一条流水线===》每个进程内至少有一个线程线程=》单指代码的执行过程进程-》资源的申请与销毁的过程
进程vs线程    1、 内存共享or隔离        多个进程内存空间彼此隔离        同一进程下的多个线程共享该进程内的数据    2、创建速度        造线程的速度要远远快于造进程

同一进程下的多个线程共享该进程内的数据:第一种方式实现线程:
 1 from threading import Thread
 2 
 3 a = 100
 4 def task():
 5     global a
 6     a = 1
 7     print("这是给子线程执行的任务!")
 8 
 9 # 创建一个子线程
10 t = Thread(target=task)
11 # 启动这个子线程
12 t.start()
13 print("主")
14 print(a)
15 
16 #这是给子线程执行的任务!
17 #主
18 #1
第二种方式实现线程:
from threading import Thread,current_thread

class MyThread(Thread):
    def run(self):
        print("run 函数执行!")
        print(current_thread())

mt = MyThread()
mt.start()

print(current_thread())
共用一个进程:
 1 import time,os
 2 
 3 def task():
 4     time.sleep(2)
 5     print("子线程 run.....")
 6     print(os.getpid())#8073
 7 
 8 t = Thread(target=task)
 9 t.start()
10 # 主线程等到子线程结束
11 t.join()
12 print("over")
13 print(os.getpid())#8073

守护线程:

#守护线程会在本进程内所有非守护的线程都死掉了才跟着死#即 守护线程其实守护的是整个进程的运行周期(进程内所有的非守护线程都运行完毕)
from threading import Thread
import time

def task():
    time.sleep(5)
    print("子线程...")

t = Thread(target=task)
t.daemon = True  # 守护线程  执行顺序与进程中一样
t.start()
print("over")

 常用方法:

 1 from threading import Thread,current_thread,active_count,enumerate
 2 import time
 3 
 4 def task():
 5     print("子线程...")
 6     time.sleep(1)
 7     # 获取当前线程对象  非常常用
 8     print(current_thread())
 9 
10 t = Thread(target=task,name="矮根线程!")
11 # t.daemon = True  # 守护线程  执行顺序与进程中一样
12 print(t.name)
13 print(t)
14 t.start()
15 # 获取当前活跃线程的数量
16 print(active_count())
17 # 返回活跃的线程对象枚举
18 print(enumerate())
19 print("over")

线程互斥锁:

 1 from threading import Thread,Lock
 2 # 创建一个互斥锁
 3 mutex = Lock()
 4 
 5 def task1():
 6     # 锁定
 7     mutex.acquire()
 8     for i in range(100):
 9         print("===================")
10     # 打开
11     mutex.release()
12 def task2():
13     mutex.acquire()
14     for i in range(100):
15         print("!!!!!!!!!!!!!!!!!!")
16     mutex.release()
17 
18 def task3():
19     mutex.acquire()
20     for i in range(100):
21         print("********************")
22     mutex.release()
23 
24 t1 = Thread(target=task1)
25 t2 = Thread(target=task2)
26 t3 = Thread(target=task3)
27 
28 t1.start()
29 t2.start()
30 t3.start()

线程死锁:

 1 from threading import Thread, Lock, current_thread, RLock
 2 import time
 3 
 4 # 叉子
 5 locka = RLock()
 6 # 盘子
 7 lockb = RLock()
 8 
 9 
10 def task1():
11     print(current_thread())
12     locka.acquire()
13     print("抢到叉子 需要盘子")
14     time.sleep(0.1)
15     lockb.acquire()
16     print("吃饭")
17 
18     lockb.release()
19     locka.release()
20 
21 
22 def task2():
23     print(current_thread())
24     lockb.acquire()
25     print("抢到盘子 需要叉子")
26     time.sleep(0.1)
27     locka.acquire()
28 
29     print("吃饭")
30     locka.release()
31     lockb.release()
32 
33 
34 t1 = Thread(target=task1)
35 t1.start()
36 t2 = Thread(target=task2)
37 t2.start()
38 
39 # 死锁发生的条件  有多个线程 多个锁   如果只有一个锁 无论是LOCK RLOK 卡不死(前提是逻辑上没有错误)
40 # RLock 就算你的代码逻辑不对 同一个线程多次对一个锁执行acquire 也不会卡死

 线程递归锁,重用锁:

#普通互斥锁不能重用,重用会阻塞# 从线程导入RLock,递归锁 重用锁,可以多次执行acquire

lock = RLock()
lock.acquire()
print("aaaaaaaaaaa")
lock.acquire()
print("bbbbbbbbbbb")

线程中使用重用锁:

import time
lock = RLock()
# 对于同一个线程而言,可以多次acquire.其他线程会被阻塞,不会锁死
def task():
    lock.acquire()
    lock.acquire()
    for i in range(5):
        time.sleep(0.5)
        print(current_thread())
    lock.release()
    lock.release()

Thread(target=task).start()
Thread(target=task).start()

event:

事件是什么?
    某件事情发生的信号
用来干什么?
    在线程间通讯,然而线程本来就能通讯
    作用只有一个就是简化代码

线程间通讯的例子:
服务器启动需要五秒
客户端启动后去连接服务器
去连接服务器必须保证服务器已经开启成功了

是否启动完成就是要通讯的内容.

注意:Event线程通讯,仅仅用于简单的条件判断,说白了代替bool类型和if判断
set()  将状态修改为True
wati() 等待状态为True才继续执行

代码实现:

原始实现:
 1 import time
 2 from threading import Thread
 3 boot = False
 4 def server_task():
 5     global boot
 6     print("正在启动....")
 7     time.sleep(5)
 8     print("启动....成功")
 9     boot = True
10 
11 def client_task():
12     while True:
13         print("连接服务器....")
14         time.sleep(1)
15         if boot:
16             print("连接成功")
17             break
18         else:
19             print("error 连接失败 服务器未启动!!")
20 
21 t1 = Thread(target=server_task)
22 t1.start()
23 
24 t2 = Thread(target=client_task)
25 t2.start()
26 
27 t1.join()
28 t2.join()
bool和if判断
event实现:
 1 # 使用事件实现
 2 
 3 import time
 4 from threading import Thread,Event
 5 event =Event()
 6 
 7 def server_task():
 8     print("正在启动....")
 9     time.sleep(5)
10     print("启动....成功")
11     event.set()
12 
13 def client_task():
14     event.wait() #一个阻塞的函数  会阻塞直到对event执行set函数为止
15     print("连接成功!")
16 
17 t1 = Thread(target=server_task)
18 t1.start()
19 t2 = Thread(target=client_task)
20 t2.start() 

信号量:

 # 信号量是控制同一时刻并发执行的任务数
 1 from threading import Thread,Semaphore,current_thread,active_count
 2 
 3 import time
 4 # 用于控制同时执行被锁定代码的线程数量,也就是线程的并发数量
 5 # 也是一种锁
 6 sm = Semaphore(5)
 7 
 8 def task():
 9     sm.acquire()
10     for i in range(10):
11         print(current_thread())
12         time.sleep(0.5)
13     sm.release()
14 
15 def task2():
16      for i in range(10):
17         print(current_thread())
18         time.sleep(0.5)
19 
20 for i in range(5):
21     Thread(target=task).start()
22     Thread(target=task2).start()
23 print(active_count())#11,包含主线程

池:

什么是?
   池:池用来限制进程/线程个数的一种机制
为何用?
   当并发的任务数远大于计算机的承受能力,应该用池的概念
   将并发的进程数/线程数控制在计算机能承受的数目

 进程池:

进程池就是一个装进程的容器
为什么出现?
    当进程很多的时候方便管理进程
什么时候用?
    当并发量特别大的时候.例如双十一
    很多时候用户停留在进程,没有别的操作,进程是空闲的,就让他进入进程池,让有任务处理时才从进程池取出来使用
进程池使用:
    ProcessPoolExecutor类
    创建时指定最大进程数,进程池会自动创建进程
    调用submit函数将任务提交到进程池中
    创建进程是在调用submit后发生的,会直接在进程池内创建对应个数的进程
有什么特点:        1.管理进程的创建        2.管理进程的销毁        3.负责任务的分配        4.控制最大并发数量注意:用池来处理TCP是不正确的,因为进程中代码执行完毕才算空闲

 基于进程池实现并发套接字通信:

服务端:
线程池默认为核数4
客户端:客户端可以同时连接服务端的最大连接数 = 线程池参数
同时只能有四个客户端能交互 

 线程池:

 1 from concurrent.futures import  ProcessPoolExecutor,ThreadPoolExecutor
 2 from threading import  current_thread
 3 
 4 import os,time,random
 5 
 6 def task():
 7     time.sleep(random.randint(1,2))
 8     print(current_thread())
 9 
10 def run():
11     # 默认为cpu核心数 
12     pool = ThreadPoolExecutor(3)
13     for i in range(30):
14         pool.submit(task)
15 
16 if __name__ == '__main__':
17     run()

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!