博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Python基础 - 11进程与线程

Posted on 2021-10-23 09:00  Kingdomer  阅读(184)  评论(0编辑  收藏  举报

Python基础 - 11进程与线程

并发: 有多个线程在操作时,如果系统只有一个CPU, 只能把CPU运行时间划分成若干个时间片,线程抢占时间片。

并行: 当系统有多个CPU时,线程操作有可能非并发,一个CPU执行一个线程,线程互不抢占。

实现多任务的方式: 1. 多进程模式  2. 多线程模式 3. 协程

进程: 计算密集型   线程: 耗时操作,爬虫 ,IO操作

一、进程 

def task1():
    while True:
        sleep(1)
        # print('--> 任务1...')
        print('--> 任务1...',os.getpid(),' -- ',os.getppid())

def task2():
    while True:
        sleep(2)
        # print('--> 任务2...')
        print('--> 任务2...',os.getpid(),' -- ',os.getppid())

# 只打印任务1
if __name__ == '__main__':
    task1()
    task2()

 

1.1 进程创建: 在Linux下使用fork函数创建进程, 在Windows系统上引用multiprocessing模块创建进程。 

process.start(): 启动进程并执行任务

process.run(): 只是执行了任务但是没有启动进程

 

from multiprocessing import Process

if __name__ == '__main__':
    p1 = Process(target=task1, name='Task 1')
    p1.start()
    print(p1.name)
    p2 = Process(target=task2, name='Task 2')
    p2.start()
    print(p2.name)

'''
--> 任务1... 4792  --  2268
--> 任务2... 3592  --  2268
--> 任务1... 4792  --  2268
--> 任务1... 4792  --  2268
--> 任务2... 3592  --  2268
--> 任务1... 4792  --  2268
'''

 

  

1.2 进程终止

def task1(s,name):
    while True:
        sleep(s)
        print('--> 任务1...',os.getpid(),' -- ',os.getppid(), name)

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

if __name__ == '__main__':
    p1 = Process(target=task1, name='Task 1', args=(1,'讲师'))
    p1.start()
    print(p1.name)
    p2 = Process(target=task2, name='Task 2', args=(2,'学生'))
    p2.start()
    print(p2.name)

    number = 0
    while True:
        number += 1
        sleep(0.2)
        if number == 20:
            p1.terminate()
            p2.terminate()
            break

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

'''
Task 1
Task 2
-----------
--> 任务1... 544  --  6800 讲师
--> 任务1... 544  --  6800 讲师
--> 任务2... 5160  --  6800 学生
'''

  

1.3 进程使用全局变量

m = 1
list_m = []

def task1(s, name):
    global m
    while True:
        sleep(s)
        m += 1
        list_m.append(str(m)+'task1')
        print('--> 任务1...,m值为 ', m, list_m)

def task2(s, name):
    global m
    while True:
        sleep(s)
        m += 1
        list_m.append(str(m) + 'task2')
        print('--> 任务2...,m值为 ', m, list_m)

if __name__ == '__main__':
    p1 = Process(target=task1, name='Task 1', args=(1, '讲师'))
    p1.start()

    p2 = Process(target=task2, name='Task 2', args=(2, '学生'))
    p2.start()

    while True:
        sleep(1)
        m += 1
        print('--> 任务main...,m值为 ', m)

'''
--> 任务main...,m值为  2
--> 任务1...,m值为  2 ['2task1']
--> 任务main...,m值为  3
--> 任务1...,m值为  3 ['2task1', '3task1']
--> 任务2...,m值为  2 ['2task2']
--> 任务main...,m值为  4
--> 任务1...,m值为  4 ['2task1', '3task1', '4task1']
--> 任务main...,m值为  5
--> 任务1...,m值为  5 ['2task1', '3task1', '4task1', '5task1']
--> 任务2...,m值为  3 ['2task2', '3task2']
--> 任务main...,m值为  6
--> 任务1...,m值为  6 ['2task1', '3task1', '4task1', '5task1', '6task1']
--> 任务main...,m值为  7
--> 任务1...,m值为  7 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1']
--> 任务2...,m值为  4 ['2task2', '3task2', '4task2']
--> 任务main...,m值为  8
--> 任务1...,m值为  8 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1']
--> 任务main...,m值为  9
--> 任务1...,m值为  9 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1']
--> 任务2...,m值为  5 ['2task2', '3task2', '4task2', '5task2']
'''

  

1.4 自定义进程

# 进程: 自定义, 继承Process,重写run()方法
class MyProcess(Process):
    def __init__(self, name):
        # 没有super,报错  AttributeError: 'MyProcess' object has no attribute '_closed'
        super(MyProcess, self).__init__()
        self.name = name

    def run(self):      
        n = 1
        while True:
            print('{} --> 自定义进程,n:{}:'.format(self.name, n))
            sleep(1)
            n += 1

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

'''
小明 --> 自定义进程,n:1:
小明 --> 自定义进程,n:2:
小明 --> 自定义进程,n:3:
小明 --> 自定义进程,n:4:
小明 --> 自定义进程,n:5:
......
'''

  

1.5 进程池: 非阻塞式

from multiprocessing import Pool
from random import random
import  time
# 非阻塞式进程: 全部添加到队列中,任务结束后,立刻返回,并没有等待其他的进程完毕。

def task(task_name):
    print('开始工作了,{},当前时间为{}'.format(task_name, time.time()))
    start = time.time()
    time.sleep(random() * 2)
    end = time.time()
    print('完成工作了,{},当前时间为{},用时为{}秒, 进程ID为{}'.format(task_name, time.time(), (end - start), os.getpid()))

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

    tasks_name = ['打篮球','写文档','听歌','看书', '打游戏', '打豆豆','豆豆哭啦']
    for task_name in tasks_name:
        pool.apply_async(task, args=(task_name,))

    pool.close()
    pool.join()  # 让主进程让步, 否则程序直接结束

    print('主进程结束啦')

'''
开始工作了,打篮球,当前时间为1633141511.4560132
开始工作了,写文档,当前时间为1633141511.4620287
开始工作了,听歌,当前时间为1633141511.4660397
开始工作了,看书,当前时间为1633141511.4715543
开始工作了,打游戏,当前时间为1633141511.4795759
完成工作了,听歌,当前时间为1633141512.0300672,用时为0.5640275478363037秒, 进程ID为824
开始工作了,打豆豆,当前时间为1633141512.0305643
完成工作了,打篮球,当前时间为1633141512.3323722,用时为0.8763589859008789秒, 进程ID为4820
开始工作了,豆豆哭啦,当前时间为1633141512.3328476
完成工作了,打豆豆,当前时间为1633141512.4075725,用时为0.37700819969177246秒, 进程ID为824
完成工作了,打游戏,当前时间为1633141512.862915,用时为1.3833391666412354秒, 进程ID为6896
完成工作了,豆豆哭啦,当前时间为1633141512.9780905,用时为0.6452429294586182秒, 进程ID为4820
完成工作了,写文档,当前时间为1633141513.0192263,用时为1.5571975708007812秒, 进程ID为6008
完成工作了,看书,当前时间为1633141513.0940337,用时为1.6224794387817383秒, 进程ID为2640
主进程结束啦
'''

 

非阻塞进程: 带回调函数  

container = []

def callback_func(n):
    container.append(n)

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

    tasks_name = ['打篮球','写文档','听歌','看书', '打游戏', '打豆豆','豆豆哭啦']
    for task_name in tasks_name:
        pool.apply_async(task, args=(task_name,), callback=callback_func(task_name))

  

1.6 进程池: 阻塞式进程

from multiprocessing import Pool
from random import random
import  time
# 阻塞式进程:  添加一个任务,执行一个任务;如果一个任务不结束另一个任务进不来

def task(task_name):
    print('开始工作了,{},当前时间为{}'.format(task_name, time.time()))
    start = time.time()
    time.sleep(random() * 2)
    end = time.time()
    print('完成工作了,{},当前时间为{},用时为{}秒, 进程ID为{}'.format(task_name, time.time(), (end - start), os.getpid()))

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

    tasks_name = ['打篮球','写文档','听歌','看书', '打游戏', '打豆豆','豆豆哭啦']
    for task_name in tasks_name:
        pool.apply(task, args=(task_name,))

    pool.close()
    pool.join()

    print('主进程结束啦')

'''
开始工作了,打篮球,当前时间为1633141835.9163513
完成工作了,打篮球,当前时间为1633141836.2893722,用时为0.37302088737487793秒, 进程ID为1644
开始工作了,写文档,当前时间为1633141836.290854
完成工作了,写文档,当前时间为1633141836.7435563,用时为0.45217323303222656秒, 进程ID为4252
开始工作了,听歌,当前时间为1633141836.745066
完成工作了,听歌,当前时间为1633141838.388317,用时为1.6432511806488037秒, 进程ID为5384
开始工作了,看书,当前时间为1633141838.3892856
完成工作了,看书,当前时间为1633141839.1293015,用时为0.740015983581543秒, 进程ID为2324
开始工作了,打游戏,当前时间为1633141839.1298025
完成工作了,打游戏,当前时间为1633141839.7122407,用时为0.5824382305145264秒, 进程ID为4864
开始工作了,打豆豆,当前时间为1633141839.7135873
完成工作了,打豆豆,当前时间为1633141839.8288903,用时为0.11530303955078125秒, 进程ID为1644
开始工作了,豆豆哭啦,当前时间为1633141839.8299038
完成工作了,豆豆哭啦,当前时间为1633141840.6465697,用时为0.8161656856536865秒, 进程ID为4252
主进程结束啦
'''

  

1.7 进程间通信

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

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())       
q.put('F') # put() 如果queue 满了,则只能等待, 除非有空 则添加成功 print(q.get(timeout=2)) print(q.get(timeout=2)) if q.full(): q.put('F', timeout=3) else: print('超时') ''' 5 A B 超时 ''' q.put_nowait('G') print(q.get_nowait()) # C

  

from multiprocessing import Process, Queue
from time import sleep
def download(q):
    images = ['abc.jpg','boy.png','girl.jpeg']
    for image in images:
        print('正在下载:', image)
        sleep(0.5)
        q.put(image)

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()

'''
正在下载: abc.jpg
正在下载: boy.png
正在下载: girl.jpeg
abc.jpg保存成功!
boy.png保存成功!
girl.jpeg保存成功!
全部保存完毕
'''

  

二、 多线程 

2.1 线程创建

import threading
from time import sleep
def download(n):
    images = ['abc.jpg','boy.png','girl.jpeg']
    for image in images:
        print('正在下载:', image)
        sleep(n)
        print('下载成功: ', image)

t = threading.Thread(target=download, name='t1', args=(1,))
t.start()

  

def listenMusic():
    musics=['ABC','星星','月亮','太阳']
    for music in musics:
        sleep(0.5)
        print('正在听{}'.format(music))

if __name__ == '__main__':
    t = threading.Thread(target=download, name='t', args=(1,))
    t.start()
    # n = 1
    # while True:
    #     print(n)
    #     n+=1
    #     sleep(1)

    t1 = threading.Thread(target=listenMusic, name='t')
    t1.start()
'''
正在下载: abc.jpg
1
2下载成功: 
 abc.jpg
正在下载: boy.png
下载成功: 3
 boy.png
正在下载: girl.jpeg
4
下载成功:  girl.jpeg
5
'''

'''
正在下载: abc.jpg
正在听ABC
下载成功:  abc.jpg
正在下载: boy.png
正在听星星
正在听月亮
下载成功:  boy.png
正在下载: girl.jpeg
正在听太阳
下载成功:  girl.jpeg
'''

  

2.2 线程状态: 新建、 就绪、 运行、 阻塞、 结束 

2.3 线程共享变量

import threading
from time import sleep
money = 1000
def run1():
    global money

    for i in range(100):
        sleep(0.1)
        money -= 1
        print('run1的money值为:',money)

def run2():
    global money

    for i in range(100):
        sleep(0.2)
        money -= 1
        print('run2的money值为:', money)

if __name__ == '__main__':
    th1 = threading.Thread(target=run1, name='th1')
    th2 = threading.Thread(target=run2, name='th2')
    th3 = threading.Thread(target=run1, name='th3')
    th4 = threading.Thread(target=run2, name='th4')
    th1.start()
    th2.start()
    th3.start()
    th4.start()

    th1.join()
    th2.join()
    th3.join()
    th4.join()

    print('money:', money)            # money: 600

 

共享变量值较大时 

import threading
n = 0

def task1():
    global n
    for i in range(10000000):
        n += 1

    print('task1 的 n 值为: ', n)


def task2():
    global n
    for i in range(10000000):
        n += 1

    print('task2 的 n 值为: ', n)

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

    th1.join()
    th2.join()
    print('主线程 的 n 值为:', n)

'''
task1 的 n 值为:  11459227
task2 的 n 值为:  11513727
主线程 的 n 值为: 11513727
'''

  

2.4 线程锁

GIL  全局解释器锁, python 底层只要用线程,就默认加锁。 

import threading, random, 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:',list1[i])
        time.sleep(0.5)
    lock.release()

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

    th2.start()
    th1.start()

    th2.join()
    th1.join()

    print('主线程 的 list1 值为:', list1)

'''
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 1
--->i: 0
--->i: 0
--->i: 0
主线程 的 list1 值为: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
'''

'''
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
主线程 的 list1 值为: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
'''

  

2.5 死锁 

# 死锁
from threading import Thread

lockA = threading.Lock()
lockB = threading.Lock()

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

    def run(self):
        if lockA.acquire():
            print(self.name + '获取了A锁')
            time.sleep(0.1)
            if lockB.acquire(timeout=2):
                print(self.name + '又获取了B锁')
                lockB.release()
            lockA.release()

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

    def run(self):
        if lockB.acquire():
            print(self.name + '获取了B锁')
            time.sleep(0.1)
            if lockA.acquire():
                print(self.name + '又获取了A锁')
                lockA.release()
            lockB.release()


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

    t1.start()
    t2.start()

    t1.join()
    t2.join()

'''
t1获取了A锁
t2获取了B锁             # 程序卡死
'''
'''
t1获取了A锁
t2获取了B锁
t2又获取了A锁           # 程序等待一段时间,结束
'''

  

2.6 生产者与消费者 

python的queue模块提供了同步的、线程安全的队列类, 包括先进先出队列Queue、LifoQueue、PriorityQueue。

__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'SimpleQueue']

# 生产者与消费者
import threading, queue, random, time

def producer(q):
    i = 0
    while i < 10:
        num = random.randint(1,100)
        q.put(num)
        print('生产者生成的数据为:%d' % num)
        time.sleep(1)
        i += 1
    q.put(None)
    # 完成任务
    q.task_done()

def consumer(q):
    while True:
        item = q.get()
        if item is None:
            break
        print('消费者消费的数据为:%s' % item)
        time.sleep(4)

    q.task_done()

if __name__ == '__main__':

    queue = queue.Queue(5)

    th1 = threading.Thread(target=producer, args=(queue,))
    th2 = threading.Thread(target=consumer, args=(queue,))

    th1.start()
    th2.start()

    th1.join()
    th2.join()

    print('over!!!')

'''
生产者生成的数据为:30
消费者消费的数据为:30
生产者生成的数据为:76
生产者生成的数据为:1
生产者生成的数据为:67
消费者消费的数据为:76
生产者生成的数据为:35
生产者生成的数据为:69
生产者生成的数据为:43
消费者消费的数据为:1
生产者生成的数据为:3
...... '''

  

按顺序打印A0,B0,A1,B1, A2,B2

# yield

def task1():
    for i in range(3):
        print('A' + str(i))
        yield
        time.sleep(0.2)

def task2():
    for i in range(3):
        print('B' + str(i))
        yield
        time.sleep(0.2)

if __name__ == '__main__':
    g1 = task1()
    g2 = task2()

    while True:
        try:
            next(g1)
            next(g2)
        except:
            break

'''
A0
B0
A1
B1
A2
B2
'''

  

三、协程 

3.1 greenlet协程

# greenlet 完成协程任务
# (venv) E:\PythonLearn\pythonBase>pip install greenlet
import time
from greenlet import greenlet

def task1():
    for i in range(3):
        print('A' + str(i))
        g2.switch()
        time.sleep(0.1)

def task2():
    for i in range(3):
        print('B' + str(i))
        g3.switch()
        time.sleep(0.1)

def task3():
    for i in range(3):
        print('C' + str(i))
        g1.switch()
        time.sleep(0.1)

if __name__ == '__main__':
    g1 = greenlet(task1)
    g2 = greenlet(task2)
    g3 = greenlet(task3)

    g1.switch()

'''
A0
B0
C0
A1
B1
C1
A2
B2
C2
'''

  

3.2 gevent协程 

# (venv) E:\PythonLearn\pythonBase>pip install gevent
import time
import gevent
from gevent import monkey

monkey.patch_all()
def task1():
    for i in range(3):
        print('A' + str(i))
        time.sleep(0.1)

def task2():
    for i in range(3):
        print('B' + str(i))
        time.sleep(0.1)

def task3():
    for i in range(3):
        print('C' + str(i))
        time.sleep(0.1)

if __name__ == '__main__':
    g1 = gevent.spawn(task1)
    g2 = gevent.spawn(task2)
    g3 = gevent.spawn(task3)

    g3.join()
    g1.join()
    g2.join()


    print('主进程结束')

'''
A0
A1
A2
B0
B1
B2
C0
C1
C2
主进程结束
'''

'''
A0
B0
C0
A1
B1
C1
A2
B2
C2
主进程结束
'''