多任务介绍
 
 
什么是多任务呢?
 
     在现实生活中 有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的; 试想,如果把唱歌和跳舞这2件事情分开依次完成的话,估计就没有那么好的效果了(想一下场景:先唱歌,然后在跳舞,O(∩_∩)O哈哈~)
 
 下面我们用程序来模拟唱歌跳舞这件事情
 
 
from time import sleep
 
def sing():
    for i in range(3):
        print("正在唱歌...%d"%i)
        sleep(1)
 
def dance():
    for i in range(3):
        print("正在跳舞...%d"%i)
        sleep(1)
 
if __name__ == '__main__':
    sing() #唱歌
    dance() #跳舞

 

 
 .运行结果如下:
 
正在唱歌...0
正在唱歌...1
正在唱歌...2
正在跳舞...0
正在跳舞...1
正在跳舞...2

 

 
 
很显然上面的程序并没有完成唱歌和跳舞同时进行的要求。如果想要实现“唱歌跳舞”同时进行,那么就需要一个新的方法,叫做:多任务
 

多任务的概念

什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。
 

现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?

答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。

真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。
 
并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
 
并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的
 
 
线程介绍
 
线程可以理解成程序中的一个可以执行的分支, 它是cup调度0的基本单元。python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的使用
 
我们可采用线程完成上面代码唱歌跳舞一起进行,如下
 
import threading
from time import sleep
 
def sing():
    for i in range(3):
        print("正在唱歌...%d"%i)
        sleep(1)
 
def dance():
    for i in range(3):
        print("正在跳舞...%d"%i)
        sleep(1)
 
if __name__ == '__main__':
    # 创建线程1
    # "target" : 线程执行的目标函数
    my_thread1 = threading.Thread(target=sing)
    my_thread2 = threading.Thread(target=dance)
    # 开启线程执行任务
    my_thread1.start()
    my_thread2.start()

 

 
 
 threading模块中的enumerate( )方法可以查看当前运行起来的线程的列表
 
threading模块中的active_count( )方法可以查看当前运行起来的线程的个数
 
注意: 线程运行起来是无序的,是由操作系统决定的,主线程须等待子线程都执行完成以后才退出程序,如下:
 
 
import time
import threading
 
 
def work1(num):
    print(threading.current_thread())
    for i in range(num):
        print("working")
        time.sleep(1)
 
 
if __name__ == '__main__':
    # 创建1
    # target=后面不要加上函数的小括号
    # args 执行函数所需要的参数
    first_thread = threading.Thread(target=work1,args=(5,))
    # 守护主线程,如果主线程退出了,那么子线程直接销毁
    first_thread.setDaemon(True)
    first_thread.start()
 
    time.sleep(2)
    print("主线等待完了")
    exit()

 

 
 
 
运行结果如下:
 
 
<Thread(Thread-1, started daemon 10500)>
working
working
主线等待完了
 

 

 线程执行代码的封装
 
由上述代码可以看出通过使用threading模块能完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写run方法。 如下
 
 
import threading
 
 
# 自定义线程
class MyThread(threading.Thread):
 
    def __init__(self, num):
        # 注意: 需要调用父类的构造方法
        super(MyThread, self).__init__()
        self.num = num
 
    # 任务1
    def work1(self):
        for i in range(self.num):
            print("我是在自定义线程里面执行的", self.name)
 
 
    # 重写run方法
    def run(self):
        self.work1()
 
 
 
# 创建线程
mythread = MyThread(5)
mythread.start()

 

 
 运行结果如下
 
我是在自定义线程里面执行的 Thread-1
我是在自定义线程里面执行的 Thread-1
我是在自定义线程里面执行的 Thread-1
我是在自定义线程里面执行的 Thread-1
我是在自定义线程里面执行的 Thread-1

 

 
 总结:
 
- 每个线程默认有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。
 
- 当线程的run()方法结束时该线程完成。
 
- 无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式
 
线程与线程间共享全局变量
 
如下:
 
 
import time
import threading
 
# 全局变量
num = 0
 
# 任务1
def work1():
    global num
    for i in range(10):
        num += 1
        time.sleep(0.1)
 
# 任务2
def work2():
    print(num)
 
if __name__ == '__main__':
    # 创建线程1
    first_thread = threading.Thread(target=work1)
    # 开启线程1
    first_thread.start()
 
    # time.sleep(2)
    # 等待线程1执行完成以后在执行下面的代码
    first_thread.join()
    print("线程1执行完成了。。。")
 
    # 创建线程2
    second_thread = threading.Thread(target=work2)
    second_thread.start()
 

 运行结果如下

 
线程1执行完成了。。。
10
 

 

 
 但是多线程间共享全局变量当数值较大时也会出现问题
 
如下:
 
 
import threading
import time
 
g_num = 0
 
def work1(num):
    global g_num
    for i in range(num):
        g_num += 1
    print("----in work1, g_num is %d---"%g_num)
 
def work2(num):
    global g_num
    for i in range(num):
        g_num += 1
    print("----in work2, g_num is %d---"%g_num)
 
print("---线程创建之前g_num is %d---"%g_num)
 
t1 = threading.Thread(target=work1, args=(1000000,))
t1.start()
 
t2 = threading.Thread(target=work2, args=(1000000,))
t2.start()
 
while len(threading.enumerate()) != 1:
    time.sleep(1)
 
print("2个线程对同一个全局变量操作之后的最终结果是:%s" % g_num)

 

 
 
运行结果如下:
 
---线程创建之前g_num is 0---
----in work1, g_num is 1199388---
----in work2, g_num is 1370989---
2个线程对同一个全局变量操作之后的最终结果是:1370989

 

 
 
为什么呢?
 
 
因为
在g_num=0时,t1取得g_num=0。此时系统把t1调度为”sleeping”状态,把t2转换为”running”状态,t2也获得g_num=0然后t2对得到的值进行加1并赋给g_num,使得g_num=1然后系统又把t2调度为”sleeping”,把t1转为”running”。线程t1又把它之前得到的0加1后赋值给g_num。这样导致虽然t1和t2都对g_num加1,但结果仍然是g_num=1
 
所以:
 
如果多个进程同时对同一个全局变量操作,会出现资源竞争问题从而
数据结果会不正确
 
 
那么 如何解决线程同时修改全局变量的问题呢?
 
首先得先了解同步的概念,同步就是协同步调,按预定的先后顺序先后依次进行运行。
同:可以理解为协同协助相互配合
如:进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B执行,再将结果给A;A再继续操作
 
解决线程同时修改全局变量的思路如下:
 
 
  1. 系统调用t1,然后获取到g_num的值为0,此时上一把锁,即不允许其他线程操作g_num
  2. t1对g_num的值进行+1
  3. t1解锁,此时g_num的值为1,其他的线程就可以使用g_num了,而且是g_num的值不是0而是1
  4. 同理其他线程在对g_num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程中不允许其他线程访问,就保证了数据的正确性
 
 
                                        互斥锁
 
 

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制

线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。

互斥锁为资源引入一个状态:锁定/非锁定

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
 

threading模块中定义了Lock类,可以方便的处理锁定:

#创建锁
mutex = threading.Lock()

#锁定
mutex.acquire([blocking])

#释放
mutex.release()

 

其中,锁定方法acquire可以有一个blocking参数

 
如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为止(如果没有指定,那么默认为True)
 
如果设定blocking为False,则当前线程不会堵塞
 
使用互斥锁完成2个线程对同一个全局变量各加100万次的操作
 
 
import threading
 
 
# 全局变量
current_num = 0
# 全局互斥锁
lock = threading.Lock()
 
# 任务1
def work1(num):
    # 上锁
    lock.acquire()
    global current_num
    for i in range(num):
        current_num += 1
    print("work1 num:", current_num)
    # 释放锁
    lock.release()
 
# 任务2
def work2(num):
    # 上锁
    lock.acquire()
    global current_num
    for i in range(num):
        current_num += 1
    print("work2 num:", current_num)
    # 释放锁
    lock.release()
 
 
 
if __name__ == '__main__':
    # 创建2个线程
    first_thread = threading.Thread(target=work1, args=(1000000, ))
    second_thread = threading.Thread(target=work2, args=(1000000, ))
 
    first_thread.start()
    second_thread.start()

 

 
 
 
运行结果如下:
 
 
work1 num: 1000000
work2 num: 2000000
 

总结

锁的好处:

  • 确保了某段关键代码只能由一个线程从头到尾完整地执行

锁的坏处:

  • 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
  • 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁
 
                              死锁
 

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。

尽管死锁很少发生,但一旦发生就会造成应用的停止响应。下面看一个死锁的例子
 
 
import threading
 
# 创建全局互斥锁
lock = threading.Lock()
 
# 取值任务
def get_value(index):
    mylist = [1,2,4,6,3]
    lock.acquire()
    if index >= len(mylist):
        print("数组越界了对应索引是:", index)
        # 假如索引越界,也要释放锁,否则其它线程一直等待锁的释放,那么这样其它线程执行不了代码
        # lock.release()
        return
    print(mylist[index])
    lock.release()
 
if __name__ == '__main__':
    for i in range(10):
        # 创建线程
        thread = threading.Thread(target=get_value, args=(i,))
        thread.start()
 

 

运行结果如下:
 
 
1
2
4
6
3
数组越界了对应索引是: 5

 

 
 
注意 此时程序并没有退出 而是处于阻塞状态了,因为当输进去的索引大于列表时return直接中止函数了并没有对锁进行释放,其他进程都在等待锁被释放。从而处于阻塞状态。
 
 
                                         进程
 
 
什么是进程,什么是程序?
 
 
程序: 例如xxx.py这是程序,是一个静态的
 
进程: 通俗来说一个程序或者软件运行起来就是叫做一个进程, 你可以想成一个公司,公司需要准备相应工作需要的资源,对应我们进程来说,同样需要准备相应资源让代码能够执行, 每个进程启动都需要向操作系统申请资源,所以进程是操作系统资源分配的一个基本单位
 
默认情况下,一个进程至少会有一个线程, 没有进程就没有线程, 因为线程是依附在进程里面的
 
 

进程的创建-multiprocessing

multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情。不仅可以通过线程完成多任务,进程也是可以的
 
 
# 之前线程可以完成多任务, 现在这个进程也可以完成多任务, 原因是进程里面默认一个条主线程在工作
 
import multiprocessing
import time
import os
 
# 任务1
def work1():
    for i in range(10):
        print("work1:------", i)
        print("work1的当前进程:", multiprocessing.current_process())
        # 获取当前进程id
        print("work1获取当前进程的进程id:", multiprocessing.current_process().pid, os.getpid())
        # 获取当前进程的父进程的id
        print("work1获取父进程的id", os.getppid())
        time.sleep(0.1)
 
 
if __name__ == '__main__':
 
    print("当前进程:", multiprocessing.current_process())
    print("获取当前进程的进程id:", multiprocessing.current_process().pid, os.getpid())
    # 创建进程
    sub_process = multiprocessing.Process(target=work1)
    # 启动进程
    sub_process.start()
 
    # 主进程的操作
    while True:
        print("我在主进程中执行。。。")
        time.sleep(0.1)
 

 

 
 
运行结果如下:
 
 
当前进程: <_MainProcess(MainProcess, started)>
获取当前进程的进程id: 5528 5528
我在主进程中执行。。。
我在主进程中执行。。。
work1:------ 0
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 1
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 2
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 3
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 4
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 5
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 6
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 7
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 8
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
work1:------ 9
work1的当前进程: <Process(Process-1, started)>
work1获取当前进程的进程id: 11828 11828
work1获取父进程的id 5528
我在主进程中执行。。。
我在主进程中执行。。。
我在主进程中执行。。。
我在主进程中执行。。。
下面会一直循环下去

 

 
Process语法结构如下:
 
target :  如果传递了函数的引用,可以任务这个子进程就执行这里的代码
 
args : 给target指定的函数传递参数,以元祖的方式传递
 
kwargs:给target指定的函数传递命名参数
 
name:给进程设定一个名字,也可以不设定
 
group:指定进程组,大多数情况下用不到
 
 
Process创建的实里对象的常用方法:
 
start():启动子进程实例(创建子进程)
is_alive( ): 判断进程子进程是否还在活着
join(【timeout】):是否等待子进程执行结束,或等待多少秒
terminate( ):不管任务是否完成,立刻终止子进程
 
 
 进程之间不共享全局变量
 
代码实例如下
 
# 进程之间不共享全局变量
import multiprocessing
import time
 
# 全局变量
mylist = list()
 
# 写入数据的任务
def write_data():
    for i in range(10):
        mylist.append(i)
        time.sleep(0.1)
    print("写入后的数据:", mylist)
 
# 读取数据的任务
def read_data():
    while True:
        print("读取全局变量的数据为:", mylist)
        time.sleep(0.5)
 
if __name__ == '__main__':
    # 创建2个进程
    write_process = multiprocessing.Process(target=write_data)
    read_process = multiprocessing.Process(target=read_data)
 
    # 开启进程
    write_process.start()
    # 等待写入进程执行完成以后再去读取数据
    write_process.join()
    read_process.start()
 
    # 进程之间不共享全局变量

 

 
 
运行结果如下:
 
 
写入后的数据: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
读取全局变量的数据为: []
读取全局变量的数据为: []
读取全局变量的数据为: []
读取全局变量的数据为: []
读取全局变量的数据为: 
......

 

 
因为进程是操作系统分配的基本单位,每创建一个进程都会向操作系统申请资源,操作系统会开辟一个新的内存空间供它使用。。就好比两个不同的房间,我给我房间买了一些家具,不关我邻居的事
 
 
给子进程制定的函数传递参数:如下
 
from multiprocessing import Process
import os
from time import sleep


def run_proc(name, age, **kwargs):
    for i in range(10):
        print('子进程运行中,name= %s,age=%d ,pid=%d...' % (name, age, os.getpid()))
        print(kwargs)
        sleep(0.2)

if __name__=='__main__':
    p = Process(target=run_proc, args=('test',18), kwargs={"m":20})
    p.start()
    sleep(1)  # 1秒中之后,立即结束子进程
    p.terminate()
    p.join()

 

 
运行结果如下:
 
 
子进程运行中,name= test,age=18 ,pid=1564...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=1564...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=1564...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=1564...
{'m': 20}
子进程运行中,name= test,age=18 ,pid=1564...
{'m': 20}

 

 
------------------------------------
 
进程和线程对比
 
    1:进程是操作系统资源分配的基本单位, 进程只提供运行所需要资源, 操作给进程分配资源
    2 :程是进程中的执行实例,可以理解一个执行的分支,默认情况下一个进程只有一个线程,也就是有一个分支, 线程是cpu调度的基本单位
    3 :程之间可以共享全局变量, 进程直接不能共享,能是独立的两个进程,只是变量名相同而已。
    4 :同一个进程中,如果进程中某一个线程挂了,那么进程直接退出死掉了, 如果在不同进程情况下,某一个进程死了, 不会影响其它进程的执行。
    5 :进程开发程序的健壮型强, 多线程开发,某一个线程挂断那么进程就退出了,健壮型没有进程强
    6 :进程需要额外分配新的运行资源,但是线程之间共享进程资源
 
 
进程间通信 ---Queue
 
 
Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信。
 
可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序,首先用一个小实例来演示一下Queue的工作原理
 
 
import multiprocessing
 
if __name__ == '__main__':
    # 创建消息队列
    # 如果指定参数,那么表示最大消息个数为3
    queue = multiprocessing.Queue(3)
    # 放置数据
    queue.put("hello")
    queue.put(23)
    # put可以放置任意对象
    queue.put([23,4345])
    # 提示: 如果这个消息队列满了,那么在put就会一直等待队列有位置了才能放入进程,否则一直等待
    # queue.put((3,32))
    # timeout: 表示超时时间,如果超过规定的时间,队列里面还没有空闲的位置,那么直接崩溃了
    #queue.put((3, 32), timeout=1)
    # 不等待,直接往队列里面放置,如果没有位置就直接崩溃
    # queue.put_nowait("word")
 
    print(queue)
    # 获取队列中的数据
    print(queue.get())
    print(queue.get())
    print(queue.get())
    # 如果队列里面没有数据了,一直等待队列中有消息了才能获取数据
    # print(queue.get())
    # timeout 超过规定时间队列里面没有数据那么直接崩溃了
    # print(queue.get(timeout=1))
    # 里面就取,消息队列没有数据就崩溃
    # queue.get_nowait()

 

 
 
运行结果如下
 
<multiprocessing.queues.Queue object at 0x00000211D76BD6A0>
hello
23
[23, 4345]
 

 

 
Queue的常用方法如下:
 
 
queue.put(timeout='' ) : 向队列中添加数据,timeout可以设置等待时间
 
queue.put_nowait( ) : 不等待直接往队列中放放置数据,如果没有位置直接崩溃
 
queue.get( ): 从队列中取数据,如果没有数据,会阻塞,直到队列中有新数据
 
queue.get_nowait( ): 不等待直接从队列中取数据,如果没有数据直接崩溃
 
queue.empty( ) : 判断队列是否为空
 
queue.full( ): 判断队列是否满了
 
Queue.qsize():返回当前队列包含的消息数量
 
 
我们可以以Queue为例,在父进程创建两个子进程,一个往Queue里写数据,一个从Queue中读数据
 
import multiprocessing
import time
 
# 任务1,写入数据
def write_data(queue):
    for i in range(10):
 
        if queue.full():
            print("消息队列满了, 不能在添加了")
            break
 
        # 向队列写入数据
        print('%s正在往队列中添加'%i)
        queue.put(i)
        time.sleep(0.1)
 
# 任务2, 读取数据
def read_data(queue):
    while True:
        if queue.empty():
            print("消息队列没有数据了。")
            break
 
        print(queue.get())
 
if __name__ == '__main__':
 
    # 创建消息队列
    queue = multiprocessing.Queue(5)
 
    # 创建2个进程
    write_process = multiprocessing.Process(target=write_data, args=(queue, ))
    read_process = multiprocessing.Process(target=read_data, args=(queue, ))
 
    # 执行进程
    write_process.start()
    # 保证写入进程执行完成以后,再取获取队列中的数据
    write_process.join()
    # 执行进程
    read_process.start()
 

 

 
 
运行结果如下:
 
 
0正在往队列中添加
1正在往队列中添加
2正在往队列中添加
3正在往队列中添加
4正在往队列中添加
消息队列满了, 不能在添加了
0
1
2
3
4
消息队列没有数据了。

进程池Pool

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务,请看下面的实例:
 
 
 
import multiprocessing
import time
import os
 
# 复制任务
def copy_work(index):
    print("正在复制中。。。。", index)
    print("获取当前进程的编号:", multiprocessing.current_process().pid, os.getpid())
    time.sleep(0.1)
 
if __name__ == '__main__':
    # 创建进程池
    # 3 表示最大有三个进程
    pool = multiprocessing.Pool(3)
 
    for i in range(10):
        # 使用进程值中的进程调用任务
        # pool.apply_async(func = copy_work, args = (i,))
        pool.apply_async(copy_work, (i,))
 
    # 提示: 主线程不会等待进程池中的任务执行完成
    # 关闭进程池,不接收其它任务了
    pool.close()
    # 等待进程池执行完成以后,代码继续执行
    pool.join()
    print("进程池中任务执行完了")

 

 
运行结果如下:
 
 
正在复制中。。。。 0
获取当前进程的编号: 1008 1008
正在复制中。。。。 1
获取当前进程的编号: 9984 9984
正在复制中。。。。 2
获取当前进程的编号: 6648 6648
正在复制中。。。。 3
获取当前进程的编号: 1008 1008
正在复制中。。。。 4
获取当前进程的编号: 9984 9984
正在复制中。。。。 5
获取当前进程的编号: 6648 6648
正在复制中。。。。 6
获取当前进程的编号: 1008 1008
正在复制中。。。。 7
获取当前进程的编号: 9984 9984
正在复制中。。。。 8
获取当前进程的编号: 6648 6648
正在复制中。。。。 9
获取当前进程的编号: 1008 1008
进程池中任务执行完了

 

进程池中的Queue

如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到一条如下的错误信息:

RuntimeError: Queue objects should only be shared between processes through inheritance.

下面的实例演示了进程池中的进程如何通信:
 
 
import multiprocessing
import time
 
# 写入数据任务1
def write_data(queue):
    for i in range(2):
        queue.put(i)
        time.sleep(0.1)
 
# 读取数据任务2
def read_data(queue):
    while True:
        if queue.empty():
            break
        print(queue.get())
 
if __name__ == '__main__':
    # 创建进程池中的queue
    queue = multiprocessing.Manager().Queue(2)
 
    # 创建进程池
    pool = multiprocessing.Pool(3)
    # 执行写入数据
    current_process = pool.apply_async(write_data, (queue,))
    # time.sleep(1)
    current_process.wait()
    pool.apply_async(read_data, (queue,))
 
    # 关闭进程池
    pool.close()
    # 等待进程池任务执行完以后在继续执行代码
    pool.join()

 

运行结果如下:
 
0
1

 

 
import time
import threading
 
 
def work1(num):
    print(threading.current_thread())
    for i in range(num):
        print("working")
        time.sleep(1)
 
 
if __name__ == '__main__':
    # 创建1
    # target=后面不要加上函数的小括号
    # args 执行函数所需要的参数
    first_thread = threading.Thread(target=work1,args=(5,))
    # 守护主线程,如果主线程退出了,那么子线程直接销毁
    first_thread.setDaemon(True)
    first_thread.start()
 
    time.sleep(2)
    print("主线等待完了")
    exit()