python--进程与线程

一进程

单核CPU实现多任务原理:操作系统轮流让各个任务交替执行
多核CPU实现多任务原理:真正的秉性执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU核心数量。所以操作系统也会自动把很多任务轮流调度到每个核心上执行。

在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
进程 > 线程 > 协程

多任务的实现有3种方式:
多进程模式;
多线程模式;
多进程+多线程模式。

多进程

进程(process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
对于操作系统来说,一个任务就是一个进程(Process)

进程创建

Process类说明

Process( target [, name [, args [, kwargs]]])

target:如果传递了函数的引用,可以任务这个子进程就执行这里的代码
args:给target指定的函数传递的参数,以元组的方式传递(可迭代的)
kwargs:给target指定的函数传递命名参数
name:给进程设定一个名字,可以不设定

Process创建的实例对象的常用方法:

start():启动子进程实例(创建子进程)
is_alive():判断进程子进程是否还在活着
join([timeout]):是否等待子进程执行结束,或等待多少秒
terminate():不管任务是否完成,立即终止子进程

Process创建的实例对象的常用属性:

name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
pid:当前进程的pid(进程号)

import os
from multiprocessing import Process
from time import sleep
def task1(s, n):
    while True:
        sleep(s)
        print('这是任务1---------', os.getpid(), '--------', os.getppid(), n)

def task2(s,n):
    while True:
        sleep(s)
        print('这是任务2---------', os.getpid(), '--------', os.getppid(), n)

number = 1
if __name__ == '__main__':
    print(os.getpid())
    #子进程1
    p = Process(target=task1, name='任务1',args=(1,'aa'))#target = 函数名
    p.start()
    print(p.name)#主进程
    #子进程2
    p1 = Process(target=task2, name='任务2',args=(2,'bb'))
    p1.start()
    print(p1.name)#主进程
    #主进程
    while True:
        number += 1
        sleep(0.2)
        if number == 100:
            p.terminate()
            p1.terminate()
            break
        else:
            print('---------->number:', number)


    print('------------')
    print('*************')


输出:
764
任务1
任务2
---------->number: 2
---------->number: 3
---------->number: 4
---------->number: 5
---------->number: 6
这是任务1--------- 21772 -------- 764 aa
---------->number: 7
---------->number: 8
---------->number: 9
---------->number: 10
---------->number: 11
这是任务2--------- 7528 -------- 764 bb
这是任务1--------- 21772 -------- 764 aa
---------->number: 12
---------->number: 13
---------->number: 14
---------->number: 15
---------->number: 16
这是任务1--------- 21772 -------- 764 aa
---------->number: 17
---------->number: 18
---------->number: 19
---------->number: 20
这是任务2--------- 7528 -------- 764 bb
---------->number: 21
这是任务1--------- 21772 -------- 764 aa
---------->number: 22
---------->number: 23
---------->number: 24
---------->number: 25
这是任务1--------- 21772 -------- 764 aa
---------->number: 26
---------->number: 27
---------->number: 28
---------->number: 29
---------->number: 30
这是任务2--------- 7528 -------- 764 bb
这是任务1--------- 21772 -------- 764 aa
……

访问全局变量:

import os
from multiprocessing import Process
from time import sleep

m = 1 #不可变类型
list1 = []  #可变类型

def task1(s, n):
    global m
    while True:
        sleep(s)
        m += 1
        list1.append(str(m) + 'task1')
        print('这是任务1---------', m, list1)

def task2(s,n):
    global m
    while True:
        sleep(s)
        m += 1
        list1.append(str(m) + 'task2')
        print('这是任务2---------', m, list1)


if __name__ == '__main__':
    print(os.getpid())
    #子进程1
    p = Process(target=task1, name='任务1',args=(1,'aa'))
    p.start()#子进程1开启

    #子进程2
    p1 = Process(target=task2, name='任务2',args=(2,'bb'))
    p1.start()#子进程2开启
    #主进程
    while True:
        sleep(2)
        m += 1
        print('--------->main', m)
输出:
这是任务1--------- 2 ['2task1']
--------->main 2
这是任务1--------- 3 ['2task1', '3task1']
这是任务2--------- 2 ['2task2']
这是任务1--------- 4 ['2task1', '3task1', '4task1']
--------->main 3
这是任务2--------- 这是任务1---------3  5 ['2task2', '3task2']['2task1', '3task1', '4task1', '5task1']

这是任务1--------- 6 ['2task1', '3task1', '4task1', '5task1', '6task1']
--------->main 4
这是任务2--------- 4 ['2task2', '3task2', '4task2']
这是任务1--------- 7 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1']
这是任务1--------- 8 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1']
--------->main 5
这是任务2--------- 5 ['2task2', '3task2', '4task2', '5task2']
这是任务1--------- 9 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1']
这是任务1--------- 10 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1']
--------->main 6
这是任务2--------- 6 ['2task2', '3task2', '4task2', '5task2', '6task2']
这是任务1--------- 11 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1']
这是任务1--------- 12 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1']
--------->main 7
这是任务2--------- 7 ['2task2', '3task2', '4task2', '5task2', '6task2', '7task2']
这是任务1--------- 13 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1', '13task1']
这是任务1--------- 14 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1', '13task1', '14task1']
--------->main 8
这是任务2--------- 8 ['2task2', '3task2', '4task2', '5task2', '6task2', '7task2', '8task2']
这是任务1--------- 15 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1', '13task1', '14task1', '15task1']
这是任务1--------- 16 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1', '13task1', '14task1', '15task1', '16task1']

自定义进程

from multiprocessing import Process
class MyProcess(Process):

    def __init__(self, name):
        super(MyProcess, self).__init__()
        self.name = name


    #重写run方法
    def run(self):
        n = 1
        while True:
            print('进程名字:'+ self.name)
            print('{}--------->自定义进程, n:{}'.format(n, self.name))
            n += 1



if __name__ == '__main__':
    p = MyProcess('小明')
    p.start()

    p1 = MyProcess('小红')
    p1.start()

进程池之非阻塞式

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果是上百个甚至上千个目标,手动的创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。
初始化Pool时,可以指定一个最大的进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行。

#1、非阻塞式进程
'''
特点:
全部添加到队列中,立刻返回,没有等待其他的进程完毕,但是回调函数是等待任务完成之后才调用。

'''
from multiprocessing import Pool
import time
from random import random
import os
#非阻塞式进程
def task(task_name):
    print('开始做任务啦!', task_name)
    start = time.time()
    #使用sleep
    time.sleep(random() * 2)
    end = time.time()
    #print('完成任务:{}  用时:{}, 进程id:{}'.format (task_name, (end - start), os.getpid()))
    return '完成任务:{}  用时:{}, 进程id:{}'.format (task_name, (end - start), os.getpid())

container = []

def callback_func(n):#回调函数必须有参数   n接收的是task返回的东西
    container.append(n)


if __name__ == '__main__':
    pool = Pool(5)

    tasks = ['听音乐', '吃饭', '洗衣服', '打游戏', '跑步', '看电影', '做饭']
    for task1 in tasks:
        pool.apply_async(task, args=(task1, ), callback=callback_func)# 非阻塞式  args进程名   callback = 回调函数名称

    pool.close()    #添加任务结束
    pool.join()     #join挡着主进程,只有进程结束主进程才能运行下一步

    for c in container:
        print(c)

    print('over!!!!')

输出:
开始做任务啦! 听音乐
开始做任务啦! 吃饭
开始做任务啦! 洗衣服
开始做任务啦! 打游戏
开始做任务啦! 跑步
开始做任务啦! 看电影
开始做任务啦! 做饭
完成任务:跑步  用时:0.04454660415649414, 进程id:2960
完成任务:看电影  用时:0.2829704284667969, 进程id:2960
完成任务:洗衣服  用时:0.4715077877044678, 进程id:2376
完成任务:听音乐  用时:1.3277170658111572, 进程id:4556
完成任务:做饭  用时:1.012617826461792, 进程id:2960
完成任务:打游戏  用时:1.4509544372558594, 进程id:12852
完成任务:吃饭  用时:1.9367280006408691, 进程id:8828
over!!!!
#2、阻塞式
'''
特点:添加一个执行一个任务,如果一个任务不结束另一个任务不添加,如果执行过的任务数量达到指定的最大值,则下一个任务则从头开始
'''
def task(task_name):
    print('开始做任务啦!', task_name)
    start = time.time()
    #使用sleep
    time.sleep(random() * 2)
    end = time.time()
    print('完成任务:{}  用时:{}, 进程id:{}'.format (task_name, (end - start), os.getpid()))
    #return '完成任务:{}  用时:{}, 进程id:{}'.format (task_name, (end - start), os.getpid())




if __name__ == '__main__':
    pool = Pool(5)

    tasks = ['听音乐', '吃饭', '洗衣服', '打游戏', '跑步', '看电影', '做饭']
    for task1 in tasks:
        pool.apply(task, args=(task1, ))# apply(self, func, args=(), kwds={})  阻塞式

    pool.close()
    pool.join()

    print('over!!!!')
输出:
开始做任务啦! 听音乐
完成任务:听音乐  用时:0.040094614028930664, 进程id:12916
开始做任务啦! 吃饭
完成任务:吃饭  用时:1.3161735534667969, 进程id:7212
开始做任务啦! 洗衣服
完成任务:洗衣服  用时:0.17168283462524414, 进程id:9800
开始做任务啦! 打游戏
完成任务:打游戏  用时:0.23629164695739746, 进程id:8140
开始做任务啦! 跑步
完成任务:跑步  用时:1.9264283180236816, 进程id:15856
开始做任务啦! 看电影
完成任务:看电影  用时:1.7075746059417725, 进程id:12916
开始做任务啦! 做饭
完成任务:做饭  用时:0.21764564514160156, 进程id:7212
over!!!!

总结:

'''
进程池:
pool = Pool(max)  创建进程池对象
添加任务:
pool.apply()  阻塞式
pool.apply_async() 非阻塞式

pool.close()  停止添加任务
pool.join()   让主进程让步,插队
'''

进程间的通信

from multiprocessing import Queue

q = Queue(5)

q.put('A')
q.put('B')
q.put('C')
q.put('D')
q.put('E')
print(q.qsize()) #qsize  队列长度
if not q.full():   #q.full()判断队列是否满了   q.empty()  判断队列是否为空
    q.put('F', timeout=3)  #put()如果queue满了则只能等待,除非有‘空地’则添加成功   put(self, obj, block=True, timeout=None)
else:
    print('队列已满')

# #取队列的值
print(q.get(timeout=2))  #get(self, block=True, timeout=None)  timeout超时 block阻塞
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
#queue.join()阻塞至队列中所有的元素都被接收和处理完毕。当条目添加到队列的时候,未完成任务的计数就会增加。每当消费者线程调用 task_done() 表示这个条目已经被回收,该条目所有工作已经完成,未完成计数就会减少。当未完成计数降到零的时候, join() 阻塞被解除。

#queue.put(item, block=True, timeout=None)  将 item 放入队列。如果可选参数 block 是 true 并且 timeout 是 None (默认),则在必要时阻塞至有空闲插槽可用。如果 timeout 是个正数,将最多阻塞 timeout 秒,如果在这段时间没有可用的空闲插槽,将引发 Full 异常。反之 (block 是 false),如果空闲插槽立即可用,则把 item 放入队列,否则引发 Full 异常 ( 在这种情况下,timeout 将被忽略)。
#queue.get(self, block=True, timeout=None)从队列中移除并返回一个项目。如果可选参数 block 是 true 并且 timeout 是 None (默认值),则在必要时阻塞至项目可得到。如果 timeout 是个正数,将最多阻塞 timeout 秒,如果在这段时间内项目不能得到,将引发 Empty 异常。反之 (block 是 false) , 如果一个项目立即可得到,则返回一个项目,否则引发 Empty 异常 (这种情况下,timeout 将被忽略)。

#queue.put_nowait()  相当于 put(item, block=False)。
#queue.get_nowait()  相当于 get(item, block=False) 。

例:

from multiprocessing import Process,Queue
from time import sleep
def download(q):
    imanges = ['girl.jpg', 'boy.jpg', 'man.jpg']
    for imange in imanges:
        print('正在下载:', imange)
        sleep(0.5)
        q.put(imange)


def getfile(q):
    while True:
        try:
            file = q.get(timeout=5)
            print('{}保存成功'.format(file))
        except:
            print('全部保存完毕')
            break




if __name__ == '__main__':
    q = Queue(5)
    p1 = Process(target=download, args=(q,))
    p2 = Process(target=getfile, args=(q,))

    p1.start()
    p1.join()

    p2.start()
    p2.join()

    print('000000000')

输出:
正在下载: girl.jpg
正在下载: boy.jpg
正在下载: man.jpg
girl.jpg保存成功
boy.jpg保存成功
man.jpg保存成功
全部保存完毕
000000000

二 多线程

'''
创建线程
    t = threading.Thread(target=download, name='aa', args=(1,))
    t.start()
    
线程状态:
   新建  就绪  运行  阻塞(阻塞结束后回到就绪状态)  结束
   
'''

threading模块

import threading
#进程:Process
# 线程:Thread
from time import sleep

def download(n):
    images = ['girl.jpg', 'boy.jpg', 'man.jpg']
    for image in images:
        print('正在下载:', image)
        sleep(n)
        print('下载{}成功'.format(image))

def listenMusic():
    musics = ['小星星', '土耳其冰激凌', '烤面筋', '烤香肠']
    for music in musics:
        sleep(0.5)
        print('正在听{}!'.format(music))



if __name__ == '__main__':

    # 线程对象
    t = threading.Thread(target=download, name='aa', args=(1,))
    t.start()

    t = threading.Thread(target=listenMusic, name='aa')
    t.start()
    n = 1
输出:
正在下载: girl.jpg
正在听小星星!
正在听土耳其冰激凌!
下载girl.jpg成功
正在下载: boy.jpg
正在听烤面筋!
下载boy.jpg成功
正在下载: man.jpg
正在听烤香肠!
下载man.jpg成功
import threading
'''
线程可以共享全局变量
'''
money = 1000

def run1():
    global money
    for i in range(100):
        money -= 1

def run2():
    global money
    for i in range(100):
        money -= 1

if __name__ == '__main__':
    th1 = threading.Thread(target=run1, name='thread1')
    th2 = threading.Thread(target=run2, name='thread2')

    th1.start()
    th2.start()
#插队
    th1.join()
    th2.join()

    print('money:', money)
'''
线程同步
GIL  全局解释器锁

'''
import threading
n = 0
def task1():
    global n
    for i in range(1000000):
        n += 1
    print('task1中n的值是:', n)

def task2():
    global n
    for i in range(1000000):
        n += 1
    print('task2中n的值是:', n)

if __name__ == '__main__':
    th1 = threading.Thread(target=task1)
    th2 = threading.Thread(target=task2)

    th1.start()
    th2.start()

    th1.join()
    th2.join()

    print('最后打印n:', n)
输出:
task1中n的值是: 1000000
task2中n的值是: 1998820
最后打印n: 1998820

三总结

'''
进程、线程:
Process类
创建进程:p = Process(target=func,name='',args=(),kwargs='')
启动进程: p.start()

自定义进程:
class MyProcess(Process):
     def run(self):
         pass
p = MyProcess()
p.start()

进程的数据共享
进程池: Pool
   阻塞式: apply(func,args,kwargs)
   非阻塞式: apply_async(func,args,kwargs,callback=函数)
进程间的通信:
Queue()
q = Queue(number)

q.put()
q.get()
q.qsize()
q.empty()
q.full()


线程:
包换关系

进程里存在多个线程,多个线程共用进程资源

t = Thread(target=func,name='',args=(),kwargs='')
t.start()

GIL  全局解释器锁

python的线程是伪线程
'''

四多线程同步

import threading
import random
import time

lock = threading.Lock()#加锁
list1 = [0]*10

def task1():
    #获取线程锁,如果已经上锁,则等待锁的释放
    lock.acquire()  #阻塞  加锁
    for i in range(len(list1)):
        list1[i] = 1
        time.sleep(0.5)
    lock.release()


def task2():
    lock.acquire()
    for i in range(len(list1)):
        print('i的值:', i)
        time.sleep(0.5)
    lock.release()

if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)


    t1.start()
    t2.start()
输出:

i的值: 0
i的值: 1
i的值: 2
i的值: 3
i的值: 4
i的值: 5
i的值: 6
i的值: 7
i的值: 8
i的值: 9

五死锁

资源分配不当,产生死锁
避免出现死锁
解决方式:
1、重构
2、acquire方法中加timeout参数
from threading import Thread,Lock
import time
lockA = Lock()
lockB = Lock()
class MyThread(Thread):
    def run(self):
        if lockA.acquire():#如果可以获取到锁则返回True
            print(self.name+'获取了A锁')
            time.sleep(0.1)
            if lockB.acquire(timeout=5):
                print(self.name+'又获取了B锁,原来还有A锁')
                lockB.release()
            lockA.release()

class MyThread1(Thread):
    def run(self):
        if lockB.acquire():#如果可以获取到锁则返回True
            print(self.name+'获取了B锁')
            time.sleep(0.1)
            if lockA.acquire(timeout=5):
                print(self.name+'又获取了A锁,原来还有B锁')
                lockA.release()
            lockB.release()


if __name__ == '__main__':
    t1 = MyThread()
    t2 = MyThread1()

    t1.start()
    t2.start()
输出:
Thread-1获取了A锁
Thread-2获取了B锁
Thread-1又获取了B锁,原来还有A锁

六生产者与消费者

两个线程之间的通信可以用队列实现

七协程

生成器完成

'''
greenlet完成协程是人工切换
'''
import  time
from greenlet import greenlet

def a():#任务A
    for i in range(5):
        print('A' + str(i))
        gb.switch()#切换到指定的线程
        time.sleep(0.1)

def b():#任务B
    for i in range(5):
        print('B' + str(i))
        gc.switch()
        time.sleep(0.1)

def c():#任务C
    for i in range(5):
        print('C' + str(i))
        ga.switch()
        time.sleep(0.1)

if __name__ == '__main__':
    ga = greenlet(a)
    gb = greenlet(b)
    gc = greenlet(c)

    ga.switch()
    gb.switch()
    gc.switch()
输出:
A0
B0
C0
A1
B1
C1
A2
B2
C2
A3
B3
C3
A4
B4
C4
'''
gevent:当一个greenlet遇到IO(指的是input output输入输出等耗时操作)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO完成,在适当的时候切换回来)
gevent自动切换协程,保证总有greenlet在执行,而不是等待IO
'''
import time
import gevent as gevent
from gevent import monkey

monkey.patch_all()#打补丁
'''
猴子补丁将标准库的部分修补为与gevent友好的函数,这些函数的行为方式与原始库相同(至少尽可能地接近)。
猴子补丁将所有耗时的操作进行切换
'''
def a():#任务A
    for i in range(5):
        print('A' + str(i))
        time.sleep(0.1)

def b():#任务B
    for i in range(5):
        print('B' + str(i))
        time.sleep(0.1)

def c():#任务C
    for i in range(5):
        print('C' + str(i))
        time.sleep(0.1)

if __name__ == '__main__':
    g1 = gevent.spawn(a)
    g2 = gevent.spawn(b)
    g3 = gevent.spawn(c)

    g1.join()
    g2.join()
    g3.join()
    print('---------------')
输出:
A0
B0
C0
A1
B1
C1
A2
B2
C2
A3
B3
C3
A4
B4
C4
---------------
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值