目录
1.3.3、所有的进程都是由另一个进程创建的,那么谁创建的第一个进程呢?
物理CPU和逻辑CPU
① 物理CPU
实际Server中插槽上的CPU个数
物理cpu数量,可以数不重复的 physical id 有几个
② 逻辑CPU
Linux用户对 /proc/cpuinfo 这个文件肯定不陌生. 它是用来存储cpu硬件信息的
信息内容分别列出了processor 0 – n 的规格。这里需要注意,如果你认为n就是真实的cpu数的话, 就大错特错了
一般情况,我们认为一颗cpu可以有多核,加上intel的超线程技术(HT), 可以在逻辑上再分一倍数量的cpu core出来
逻辑CPU数量=物理cpu数量 x cpu cores 这个规格值 x 2(如果支持并开启ht)
备注一下:Linux下top查看的CPU也是逻辑CPU个数
③ CPU核数
一块CPU上面能处理数据的芯片组的数量、比如现在的i5 760,是双核心四线程的CPU、而 i5 2250 是四核心四线程的CPU
一般来说,物理CPU个数×每颗核数就应该等于逻辑CPU的个数,如果不相等的话,则表示服务器的CPU支持超线程技术
对于单核CPU同一时刻只能有一个任务执行
并发:交替执行(某时间段内的处理能力)
并行:同时执行(要有多核支撑)
1.管道(本质使用环形队列来实现的,一侧写,一侧读)
匿名管道:亲缘进程之间才能够通信(父进程创建子进程时,子进程会拷贝父进程所有的内容。所以只有子进程才知道父进程是在哪里打开的管道。)
命令管道:没有亲缘关系进程也能通信
进程不起来的时候,管道就会关掉
2.信号(由内核去做的)
通过硬件。如:ctrl+c;通过软件 kill
有些信号是可以拒绝的,比如说15号信号
信号本质就是修改程序pcb
hup信号--hohup
关闭子进程(发送了term信号)和reload(本质上是给进程发送了hup信号)
- [root@ngingx-kafka01 ~]# kill -l
- 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
- 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
- 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
- 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
- 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
- 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
- 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
- 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
- 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
- 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
- 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
- 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
- 63) SIGRTMAX-1 64) SIGRTMAX
-
- ExecReload= :/bin/sh -C "/bin/kill -s HUP $(/bin/cat /var/ run/nginx. pid) "
- ExecStop=/bin/sh -C "/bin/kill -S TERM $(/bin/cat /var/ run/nginx. pid) "
3.信号量
是一种锁,可以规定同一时刻有几个程序可以访问这个共享内存。信号量一般搭配共享内存使用。
4.共享内存
最快的通信方式
- [root@ngingx-kafka01 ~]# ipcs -m
-
- ------------ 共享内存段 --------------
- 键 shmid 拥有者 权限 字节 nattch 状态
5.socket
一般用于不同主机的不用进程通信。也可以用在同一种主机不用进程之间的通信。
6.消息队列
进程通信的前提:进程之间都是相互独立的
定义:进程就是正在运行的程序,是计算机进行资源(计算资源和存储资源)分配的最小单位
开机启动会启动两个进程:systemd(后续用来创建用户进程,pid号为1)和kthreadd(后续用来创建内核进程,pid号为2)
进程组成:PCB(是进程的唯一标识,是进程的组成核心,是一个数据结构)+数据段+代码段
1、pid 进程唯一标识符
2、有效用户信息 -euid,egid(通常情况下就是uid,gid)
3、程序的状态
4、程序优先级
5、程序的上下文切换
内存空间分为用户空间和内核空间。
cpu两种状态:用户态(运行普通程序)和内核态(运行操作系统程序)
线程是运行在进程之上的。是操作系统进行调用的最小单位(是最小的运行单位)
总结:多线程是异步的,但这不代表多线程真的是几个线程是在同时进行,实际上是系统不断地在各个线程之间来回的切换(因为系统切换的速度非常的快,所以给我们在同时运行的错觉)
进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。凡是用于完成操作系统的各种功能的进程就是系统进程,而所有由你启动的进程都是用户进程。
同理,多进程就是指计算机同时执行多个进程,一般是同时运行多个软件。
共性:多进程和多线程都是用来提高素速度的
区别:
1、一个进程是可以有一个以上的线程,这些线程都是共享内存空间的。
2、不同进程之间内存空间都是独立的
3、创建新的线程很简单,但是创建一个新的进程需要对其父进程进行一次克隆。
4、一个线程可以控制和操作同一个进程里的其他线程。进程只能操作子进程
5、一个主线程改变,可能会影响其他线程,改变父进程不会影响子进程。
6、线程是看不到的,进程能够看到。
7、安全性来说:多进程会比多线程安全一些
一般来说,多线程开销会比多进程少。
多线程的问题是多个人同时吃一道菜的时候容易发生争抢,例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了。。。此时就必须等一个人夹一口之后,在还给另外一个人夹菜,也就是说资源共享就会发生冲突争抢。
1。对于 Windows 系统来说,【开桌子】的开销很大,因此 Windows 鼓励大家在一个桌子上吃菜。因此 Windows 多线程学习重点是要大量面对资源争抢与同步方面的问题。
2。对于 Linux 系统来说,【开桌子】的开销很小,因此 Linux 鼓励大家尽量每个人都开自己的桌子吃菜。这带来新的问题是:坐在两张不同的桌子上,说话不方便。因此,Linux 下的学习重点大家要学习进程间通讯的方法。
开桌子的意思是指创建进程。开销这里主要指的是时间开销。
可以做个实验:创建一个进程,在进程中往内存写若干数据,然后读出该数据,然后退出。此过程重复 1000 次,相当于创建/销毁进程 1000 次。在我机器上的测试结果是:
UbuntuLinux:耗时 0.8 秒 Windows7:耗时 79.8 秒 两者开销大约相差一百倍。
这意味着,在 Windows 中,进程创建的开销不容忽视。换句话说就是,Windows 编程中不建议你创建进程,如果你的程序架构需要大量创建进程,那么最好是切换到 Linux 系统。

三态模型

引起进程状态转换的具体原因如下:
五态模型
引起进程状态转换的具体原因如下:
NULL→新建态:执行一个程序,创建一个子进程。
新建态→就绪态:当操作系统完成了进程创建的必要操作,并且当前系统的性能和虚拟内存的容量均允许。
运行态→终止态:当一个进程到达了自然结束点,或是出现了无法克服的错误,或是被操作系统所终结,或是被其他有终止权的进程所终结。
运行态→就绪态:运行时间片到;出现有更高优先权进程。
运行态→等待态:等待使用资源;如等待外设传输;等待人工干预。
就绪态→终止态:未在状态转换图中显示,但某些操作系统允许父进程终结子进程。
等待态→终止态:未在状态转换图中显示,但某些操作系统允许父进程终结子进程。
终止态→NULL:完成善后操作。
1、R (TASK_ RUNNING,是为状态标识state值), 可执行状态。
2、S (TASK_ INTERRUPTIBLE), 可中断的睡眠状态
3、D (TASK_ UNINTERRUPTIBLE), 不可中断的睡眠状态
4、T/t (TASK STOPPED or TASK TRACED),暂停状态或跟踪状态
5、Z (TASK_ DEAD - EXIT ZOMBIE),退出状态,进程成为僵尸进程。
- # 编辑一个死循环脚本,并执行
- #!/bin/bash
- while :
- do
- echo "hello"
- sleep 1
- done
按ctrl + z 暂停任务
- [root@nginx-kafaka03 ~]# jobs # 查看暂停的任务
- [1]+ 已停止 bash text.sh
- [root@nginx-kafaka03 ~]# fg # 运行停止的任务
- bash text.sh
- hello
- hello
- hello
首先,清楚什么是上下文
每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,这就涉及到CPU 寄存器 和程序计数器(PC):
这两个是 CPU 运行任何任务前都必须依赖的环境,因此叫做CPU上下文。
定义:(程序之间的互相切换,时间片到了之后,保存当前的上下文,进入下一个程序)
需要履行的步骤:
被保存起来的上下文会存储到系统内核中,等待任务重新调度执行时再次加载进来。
CPU 的上下文切换分三种:进程上下文切换、线程上下文切换、中断上下文切换。
Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间:
进程可以在用户空间运行(叫作:进程用户态),也可以在内核空间运行(叫作:进程内核态)。从用户态到内核态需要系统调用完成。
系统调用过程中也会发生 CPU 上下文切换。CPU 寄存器会先保存用户态的状态,然后加载内核态相关内容。系统调用结束之后,CPU 寄存器要恢复原来保存的用户态,继续运行进程。所以,一次系统调用,发生两次 CPU 上下文切换。
需要注意的是,系统调用过程中,不涉及虚拟内存等进程用户态的资源,也不会切换进程。与通常所说的进程上下文切换不同:
进程是由内核管理和调度的,进程的切换只能发生在内核态。因此,进程的上下文不但包括虚拟内存、栈、全局变量等用户空间资源,还包括内核堆栈、寄存器等内核空间状态。所以,进程的上下文切换比系统调用多一个步骤:保存当前进程的内核状态和 CPU 寄存器之前,先把该进程的虚拟内存、栈等保存起来;加载下一个进程的内核态后,还需要刷新进程的虚拟内存和用户栈。保存上下文和恢复上下文需要内核在 CPU 上运行才能完成。
进程切换时需要切换上下文,进程切换的场景有:
系统负载(System Load)是系统CPU繁忙程度的度量,即有多少进程在等待被CPU调度(进程等待队列的长度)。
平均负载(Load Average)是一段时间内系统的平均负载,这个一段时间一般取1分钟、5分钟、15分钟。根据就绪+运行状态 线程队列的情况来反映出CPU一段时间内的繁忙程度
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。
用户和应用程序可以通过proc得到系统的信息,并可以改变内核的某些参数。由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的。
- [root@localhost ~]# ls /proc
- 1 1656 27 3 404 494 599 691 consoles kallsyms mtrr timer_list
- 10 1673 279 30 405 5 6 693 cpuinfo kcore net timer_stats
- 1003 1676 28 301 406 50 601 7 crypto keys pagetypeinfo tty
- 103 1678 281 302 407 51 610 8 devices key-users partitions uptime
- 11 1698 283 303 408 515 620 9 diskstats kmsg sched_debug version
- 12 18 284 35 409 518 621 957 dma kpagecount schedstat vmallocinfo
- 120 19 286 36 410 52 646 958 driver kpageflags scsi vmstat
- 1252 2 287 37 411 53 66 961 execdomains loadavg self zoneinfo
- 13 20 288 377 412 584 669 980 fb locks slabinfo
- 1352 21 289 378 413 585 670 acpi filesystems mdstat softirqs
- 1375 22 29 38 414 590 671 asound fs meminfo stat
- 1376 23 290 389 46 593 672 buddyinfo interrupts misc swaps
- 14 24 291 390 47 594 675 bus iomem modules sys
- 15 25 293 4 48 596 677 cgroups ioports mounts sysrq-trigger
- 16 26 296 403 49 597 679 cmdline irq mpt sysvipc
这些以数字命名的目录,它们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在/proc下,以进程的 PID号为目录名,它们是读取进程信息的接口。而self目录则是读取进程本身的信息接口,是一个link。
线程
线程:线程被称为轻量级进程(Lightweight Process,LWP),是cpu调度的基本单位
组成:线程ID、当前指令指针(PC)、寄存器集合、堆栈组成
在单个程序中同时运行多个线程完成不同的工作,称为多线程。
threading的功能
threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。
threading模块提供的常用类:
- import requests
- import time
- import functools
- import threading
-
-
- def get_content(url):
- text = requests.get(url).content
- time.sleep(0.5)
- print("get content")
-
-
- def runtime(func):
- # 保留传递进来的函数的元数据,将他的元数据赋值给inner
- # 元数据:函数名字、注释……
- @functools.wraps(func)
- def inner(*args, **kwargs):
- start = time.time()
- result = func(*args, **kwargs)
- end = time.time()
- print(f"函数执行花了{end - start}s")
- return result
-
- return inner
-
-
- @runtime
- def main():
- t_list = []
- for i in range(5):
- # get_content("https://www.baidu.com")# 若是只是用这行代码,后面的代码不使用,那么就是一个人做五个事情
- # 创建线程
- # target --》指定传入的方法的名字,而不是调用的结果。
- # args --》指定方法需要传入的参数 元组类型
- t = threading.Thread(target=get_content, args=("https://www.baidu.com",))
- t_list.append(t) # 把创造的每个线程实例都加到列表中去
- # 默认是前台线程,主线程执行完了,等待子线程执行完再退出。
- t.setDaemon(True) # 设置后台线程,主线程退出子线程也退出
- t.start() # 启动线程 --> 自动执行run方法 线程准备就绪,等待CPU调度,start会自动调用t.run()
- # t.join() # 若是把阻塞放在这里,那么这个多线程就没有意义了。
-
- # 这部分代码是主线程来做
- for t in t_list:
- # 阻塞当前环境上下文,直到t的线程执行完成
- # 谁执行了这个join代码,谁就是当前环境
- # t.join() # 若是t运行完成之后,main会继续往下运行;若是t没有运行完成,main会阻塞
- t.join(timeout=3) # 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout
-
- print("ending")
-
-
- main()
-
- ####### 执行结果
- get content
- get content
- get content
- get content
- get content
- ending
- 函数执行花了1.3826136589050293s
- import threading
-
-
- class MyThread(threading.Thread):
- def __init__(self, num):
- super(MyThread, self).__init__()
- self.num = num
-
- def run(self):
- print(f"running on numbers:{self.num}")
-
-
- t1 = MyThread(1)
- t2 = MyThread(2)
- t1.start()
- t2.start()
作用:主要是解决资源争用、数据读取不一致等问题。
加锁的前提:在线程需要时使用公共资源的时候
类型:
LOCK 原始锁 ,获取锁之前不做判断,直接获取到锁的位置
RLOCK 重入锁 获取锁之间先判断,如果自己有了锁就立即返回
示例使用公共资源没有使用锁的情况
- import threading
- import time
-
- # 这样会出现读脏数据
- num = 0
- def sum_num(i):
- global num
- time.sleep(0.5)
- num += i
- print(num)
-
- for i in range(5):
- t = threading.Thread(target=sum_num, args=(i,))
- t.start()
-
- ####### 执行结果,这个结果是随机的,这只是一种结果
- 269
-
- 1010
使用锁的例子
- import threading
- import time
-
- #只需要在有公共资源的地方加锁
- num = 0
- def sum_num(i):
- lock.acquire() # 获取锁
- global num
- time.sleep(0.5)
- num += i
- print(num)
- lock.release() # 释放锁
-
- def sum_num(i):
- with lock: # 自动获取和释放锁
- global num
- time.sleep(0.5)
- num += i
- print(num)
-
-
- lock = threading.Lock() # 创建一个锁对象 也可以这样写:threading.RLock()
- for i in range(5):
- t = threading.Thread(target=sum_num, args=(i,))
- t.start()
-
- #### 执行结果
- 0
- 1
- 3
- 6
- 10
示例:使用原始锁
- import threading
- lock1 = threading.Lock() # 只会执行第一步"lock1 acquire"
- lock1.acquire()
- print("lock1 acquire")
- lock1.acquire()
- print("lock1 acquire 2")
- lock1.release()
- print("lock1 release")
- print("lock1 release 2")
-
-
- #### 执行结果
- lock1 acquire
示例:使用重入锁
- import threading
-
- lock1 = threading.RLock() # 这个能够执行完毕
- lock1.acquire()
- print("lock1 acquire")
- lock1.acquire()
- print("lock1 acquire 2")
- lock1.release()
- print("lock1 release")
- print("lock1 release 2")
-
-
- ### 执行结果
- lock1 acquire
- lock1 acquire 2
- lock1 release
- lock1 release 2

linux线程通信:互斥锁+信号量+条件变量
- # 这个是信号量的例子
- import threading
- import time
-
- num = 0
-
- def sum_num(i):
- with lock: # 自动获取和释放锁
- global num
- print(f"this is thread {i}")
- time.sleep(5)
- num += i
- print(num)
-
-
- lock = threading.BoundedSemaphore(2) # 定义了两把钥匙,同时有两个线程去执行
- for i in range(5):
- t = threading.Thread(target=sum_num, args=(i,))
- t.start()
执行结果
- this is thread 0
- this is thread 1
- 0
- this is thread 2
- 1
- this is thread 3
- 36
-
- this is thread 4
- 10

Python中多线程
os.fork中就用来创建子进程的方法
注意:这个os.fork()方法只有在unix系统中才会有,在window下没有。
• 使用fork创建子进程后,操作系统会将当前的进程复制一份
• 原来的进程称为父进程,新创建的进程称为子进程
• 两个进程会各自互不干扰的执行下面的程序
• 父进程与子进程的执行顺序与系统调度有关
• 在子进程内,这个方法会返回0;在父进程内,这个方法会返回子进程的编号PID。
• 返回值为大于0时,此进程为父进程,且返回的数字为子进程的PID;
• 当返回值为0时,此进程为子进程。
• 如果返回值为负数则表明创建子进程失败。
• 父进程结束时,子进程并不会随父进程立刻结束。同样,父进程不会等待子进程执行完。
• os.getpid():获取进程的进程号。
• os.getppid():获取父进程的进程号。
- # 这段代码要放到unix环境里执行,我们可以把它放到linux环境中
- import os, time
-
- print("start...")
-
- result = os.fork() # 系统调用让内核返回的值
- # 父进程运行时得到的pid为子进程的pid
- # 子进程运行时这个pid就是0
- print("outerside pid is:", result)
- if result == 0:
- print("child paocess")
- time.sleep(60)
- print("child pid is:", os.getpid())
- print("child-parent pid is :", os.getppid())
- else:
- print("parent process")
- time.sleep(60)
- print("parent pid is :", os.getpid())
得到的结果:
- [root@nginx-kafaka03 22-08-10]# ps -ef|grep os-fork
- root 1997 1947 1 15:20 pts/0 00:00:00 python3 os-fork.py
- root 1998 1997 0 15:20 pts/0 00:00:00 python3 os-fork.py
- root 2000 1912 0 15:20 pts/3 00:00:00 grep --color=auto os-fork
-
-
- [root@nginx-kafaka03 22-08-10]# python3 os-fork.py
- start...
- outerside pid is: 1998
- parent process
- outerside pid is: 0
- child paocess
- parent pid is : 1997
- child pid is: 1998
- child-parent pid is : 1997
定义:子进程退出,父进程没有响应,父进程没有去调用wait()或者waitpid()去获取子进程的状态。子进程的进程控制块就依然会保存在系统中,这种进程就称之为僵尸进程。僵尸进程只有会少量的元数据残留。
制造僵尸进程
- # 这段代码要放到unix环境里执行,我们可以把它放到linux环境中
- import os, time
-
- print("start...")
-
- result = os.fork() # 系统调用让内核返回的值
- # 父进程运行时得到的pid为子进程的pid
- # 子进程运行时这个pid就是0
- print("outerside pid is:", result)
- if result == 0:
- print("child paocess")
- # time.sleep(60) 把这段注释掉
- print("child pid is:", os.getpid())
- print("child-parent pid is :", os.getpid())
- else:
- print("parent process")
- time.sleep(60)
- print("parent pid is :", os.getpid())
- [root@nginx-kafaka03 22-08-10]# ps aux|grep python3
- root 2087 0.0 0.3 125188 5752 pts/0 S+ 15:40 0:00 python3 os-fork.py
- root 2088 0.0 0.0 0 0 pts/0 Z+ 15:40 0:00 [python3]
- root 2095 0.0 0.0 112824 988 pts/3 S+ 15:40 0:00 grep --color=auto python
-
- [root@nginx-kafaka03 22-08-10]# python3 os-fork.py
- start...
- outerside pid is: 2088
- parent process
- outerside pid is: 0
- child paocess
- child pid is: 2088
- child-parent pid is : 2088
- parent pid is : 2087
定义:一个父进程退出,子进程还在运行,那么这个子进程就会称为孤儿进程。孤儿进程会被pid为1的进程所收养。
制造孤儿进程
- import os, time
-
- print("start...")
-
- pid = os.fork()
- print("outerside pid is:",pid)
- if pid == 0:
- print("child paocess")
- time.sleep(30)
- print("child pid is:", os.getpid())
- print("child-parent pid is :", os.getpid())
- else:
- print("parent process")
- print("parent pid is :", os.getpid())
- [root@nginx-kafaka03 22-08-10]# ps -ef|grep python3
- root 3355 1 0 17:05 pts/0 00:00:00 python3 os-fork.py # 这里说明这个进程是一个孤儿进程
- root 3357 1912 0 17:05 pts/3 00:00:00 grep --color=auto python3
语法
构造方法:Process([group [, target [, name [, args [, kwargs]]]]])
• group: 线程组,目前还没有实现,库引用中提示必须是None;
• target: 要执行的方法;
• name: 进程名;
• args/kwargs: 要传入方法的参数
实例方法
• p.start():启动进程,并调用该子进程中的p.run()
• p.run(): strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法
• p.terminate(): 不管任务是否完成,立即停止工作进程
• p.is_alive(): 如果p仍然运行,返回True
• p.join([timeout]): 阻塞当前上下文环境的进程,直到调用此方法的进程终止或到达指定的timeout
- from multiprocessing import Process, current_process
- import time
-
- lst = []
- def task(i):
- print(current_process().name, i, 'start.....')
- time.sleep(2)
- lst.append(i)
- print(lst)
- print(current_process().name, i, 'end...')
-
-
- if __name__ == "__main__": # 多进程必须有这个__name__= "__main__",必须是主程序运行多进程
- p_lst = []
- for i in range(4):
- p = Process(target=task, args=(i,))
- p_lst.append(p)
- p.start()
- for p in p_lst:
- p.join()
- print("main end .......")
- # 执行结果
- Process-2 1 start.....
- Process-1 0 start.....
- Process-3 2 start.....
- Process-4 3 start.....
- [1]
- Process-2 1 end...
- [0]
- Process-1 [2]0
- end...
- Process-3 2 end...
- [3]
- Process-4 3 end...
- main end .......
-
- Process finished with exit code 0
自定义进程类创建多进程
- class MyMultip(Process):
- def __init__(self,num):
- super(MyMultip, self).__init__()
- self.num = num
- def run(self) -> None:
- print(f"running on numbers:{self.num}")
-
-
- if __name__ == "__main__": # 多进程必须有这个__name__= "__main__",必须是主程序运行多进程
- t1 = MyMultip(1)
- t2 = MyMultip(2)
- t1.start()
- t2.start()
-
- # 运行结果
- running on numbers:1running on numbers:2
不同进程间内存是不共享的,multiprocessing中提供以下方式实现进程间的数据交换
使用multiprocessing.Manager共享数据
- from multiprocessing import Manager, Process,Lock
- import time
-
-
- def func(i, temp):
- with lock:
- temp[0] += 100
- print(i, "------------------>", temp[0])
- time.sleep(1)
-
-
-
- # 使用manager,父进程要等待子进程结束再退出
- lock = Lock()
- if __name__ == '__main__':
- manager = Manager()
- temp = manager.list([1, 2, 3]) # 要传递什么类型就在manager.后面放什么类型
- p_list = []
- for i in range(10):
- p = Process(target=func, args=(i, temp))
- p.start()
- p_list.append(p)
- for i in p_list:
- i.join()
-
- # 运行结果之一
- # """
- # 不加锁可能会出现脏数据
- # 0 ------------------> 101
- # 2 ------------------> 201
- # 4 ------------------> 201
- # 1 ------------------> 301
- # 3 ------------------> 401
- # 5 ------------------> 501
- # 6 ------------------> 601
- # 7 ------------------> 701
- # 8 ------------------> 801
- # 9 ------------------> 901
- # """
- # 加锁之后
- from multiprocessing import Manager, Process,Lock
- import time
-
-
- def func(i, temp):
- with lock:
- temp[0] += 100
- print(i, "------------------>", temp[0])
- time.sleep(1)
-
-
-
- # 使用manager,父进程要等待子进程结束再退出
- lock = Lock()
- if __name__ == '__main__':
- manager = Manager()
- temp = manager.list([1, 2, 3]) # 要传递什么类型就在manager.后面放什么类型
- p_list = []
- for i in range(10):
- p = Process(target=func, args=(i, temp))
- p.start()
- p_list.append(p)
- for i in p_list:
- i.join()
-
- # 运行结果
- 1 ------------------> 101
- 2 ------------------> 201
- 0 ------------------> 301
- 4 ------------------> 401
- 3 ------------------> 501
- 9 ------------------> 601
- 5 ------------------> 701
- 6 ------------------> 801
- 8 ------------------> 901
- 7 ------------------> 1001
manager使用socket实现的数据共享,父进程结束,manager进程会先退出
-
- from multiprocessing import Manager, Process,Lock
- import time
-
-
- def func(i, temp):
- time.sleep(50)
- with lock:
- temp[0] += 100
- print(i, "------------------>", temp[0])
-
-
-
- # 使用manager,父进程要等待子进程结束再退出
- # 使用socket方式实现
- lock = Lock()
- if __name__ == '__main__':
- manager = Manager()
- temp = manager.list([1, 2, 3]) # 要传递什么类型就在manager.后面放什么类型
- p_list = []
- for i in range(10):
- p = Process(target=func, args=(i, temp))
- p.start()
- p_list.append(p)
- # for i in p_list:
- # i.join() # 不加join,manager会提前退出
-
- # 执行结果
- [root@nginx-kafaka03 22-08-10]# ps -ef|grep python3
- root 3605 1912 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3611 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3612 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3613 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3615 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3616 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3617 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3618 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3620 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3621 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3622 3605 0 21:43 pts/3 00:00:00 python3 test_nojoin.py
- root 3634 1947 0 21:43 pts/0 00:00:00 grep --color=auto python3
- [root@nginx-kafaka03 22-08-10]# ps aux|grep python3|wc -l
- 12
-
-
- # 报错信息中可以在直到manager使用的是socket方式实现的数据共享
- File "/usr/lib64/python3.6/multiprocessing/connection.py", line 491, in Client
- c = SocketClient(address)
使用multiprocessing.Queue共享数据
Queue不能再Pool进程池中使用,使用Multiprocessing.Manager类可以适用Pool类
- from multiprocessing import Process, Queue
- import time
- def func(i, q):
- if not q.empty():
- print(i, "-->get value", q.get())
- time.sleep(2)
- # 队列先进先出
- if __name__ == '__main__':
- q = Queue() # 不能用在进程池
- for i in range(6):
- q.put(10 - i)
- p = Process(target=func, args=(i, q))
- p.start()
-
-
- # 执行结果
- 0 -->get value 10
- 4 -->get value 9
- 1 -->get value 8
- 3 -->get value 7
- 2 -->get value 6
- 5 -->get value 5
进程池的作用:有效的降低频繁创建销毁线程所带来的额外开销
最简单、最常用的算法是随机算法和Round Robin(轮流算法)
构造方法
实例方法
注意: