Python多任务编程

进程、线程

Posted by 新宇 on January 6, 2020

一、多任务

1. 多任务

在同一时间段,同时执行多个任务

2. 并发

任务数量 > CPU核心数量,CPU核心在同一时间段交替执行

3. 并行

任务数量 < CPU核心数量,同一时刻执行多个任务(指多核情况下)

二、进程

1.概念:

进程是操作系统调度和分配资源的最小单位

2.特点

  • 程序开启后默认开启一个进程
  • 开启多个进程执行任务效率更高
  • 开启进程后默认开启一个主线程

3.实现多进程

任务调用单位:函数

# 1. 导入进程包

import multiprocessing  

# 2. 通过进程类创建进程对象(func是函数名,可省略:name进程名,group进程组) 

def func():  
	print("music.....")  
p = multiprocessing.Process(target=func, name=proc_name, group=None)  

# 3. 启动进程  

p.start()  

4. 面向对象实现

import multiprocessing 

class MyProcess(multiprocessing.Process):
    def __init__(self):
        super().__init__()

    def music(self):
        print('music...')

    def run(self) -> None:
    	self.music()

m = MyProcess()

m.start()

5. 进程传递参数

  • 通过元组 multiprocessing.Process(target=func, args=(3,))
  • 通过dict multiprocessing.Process(target=func, kwargs={“num”: 3})

6. 获取进程编号

  1. 父进程:创建当前进程的进程,称为父进程;
  2. 获取当前进程编号

     # 获取当前进程编号
    
     os.getpid()
    
     # 查看进程名
    
     multiprocessing.current_process().name
    
  3. 获取父进程编号

     os.getppid()
    

7. 进程间不共享全局变量

  1. 创建子进程会对主进程资源进行拷贝

  2. 进程join:把进程原来的并行执行改成串行

     import multiprocessing
     import time
    
     l = []
    
     def write_data():
         for i in range(10):
             l.append(i)
         print(l)
    
    
     def read_data():
         print(l)
    
    
     if __name__=="__main__":
         print('主进程开始!')
         p1 = multiprocessing.Process(target=write_data)
         p2 = multiprocessing.Process(target=read_data)
         p1.start()
         # join 将原来的并行改为串行,即将p1执行完之后,再进行后续操作
         p1.join()
         # time.sleep(1)
         p2.start()
         p2.join()
         print('主进程结束!')
    
  3. 主进程会等到所有子进程结束后再结束

8. 主进程和子进程的执行顺序

  1. 为了保证子进程能正常运行,主进程会等到所有子进程执行结束后再销毁

  2. 如果想要主进程执行结束后结束子进程,可以通过守护进程或者手动销毁子进程

     # 设置守护主进程的方式:
    
     子进程对象.daemon = True
    
     # 销毁子进程的方式
    
     子进程对象.terminate()
    
    

9. 进程相关Linux命令

```
# 获取进程信息

ps -ef | grep python

ps -aux | grep python

# 杀死进程

kill -9 进程号
```

三、线程

1. 多线程创建

# 1.导入模块

import threading

# 2. 创建thread

t = threading.Thread(target=func, args=(1,), kwargs={})

# 3. 开启start

t.start()

2. 多线程传参

  • args: 以元组的方式(必须和函数的参数顺序一致)
  • kwargs: 以字典的方式

3. 线程的执行顺序

  • 需要主线程结束,停止子线程
  • 线程间执行是无序的,是由CPU调度决定的
# 守护线程:主线程执行完毕,子线程也结束

t = threading.Thread(target=func, daemon=True)

# 线程串行执行

t.join()

# 获取当前线程的名称

threading.current_thread().name

4.线程间共享全局变量

import threading
import time
l = []

def write_data(num):
    for i in range(num):
        l.append(i)
    print('write_data:', l)

def read_data(count):
    # print(count)
    print('read_data:', l)

if __name__ == '__main__':
    wp = threading.Thread(target=write_data, args=(10,))
    rp = threading.Thread(target=read_data, kwargs={'count': 20})
    wp.start()
    time.sleep(1)
    rp.start()
    print('主进程结束')

四、锁

1. 线程间访问全局变量错误

原因:操作非原子性(不是一步就能搞定),例如 a+=1

解决方案:把操作进行同步,使用同步锁

2.互斥锁

# 1.创建锁

mutex = threading.lock()

# 2.上锁

mutex.acquire()

# 3. 释放锁

mutex.release()

3.死锁

申请和释放锁是成对出现的,如果线程中只申请锁,不释放锁,就会产生死锁,程序会停止运行

五、线程VS进程

  1. 进程是系统分配和调度资源的最小单位,线程是CPU执行的最小单位
  2. 创建进程消耗资源大,线程消耗小
  3. 进程不能访问全局变量,线程可以
  4. 优先使用线程,其次考虑进程
  5. 线程不能独立运行,必须依附于进程而存在的,一个进程默认有一个线程,进程可以创建多个线程
  6. 进程可以使用多核,线程中不能使用多核(特指Python的CPython解释器)

六、进程通信方式

  1. 管道
  2. 消息队列
  3. 共享内存
  4. 信号量
  5. Socket