python进程,线程以及协程

进程

进程的概念

​ 一个运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位。一个进程创建时,操作系统就会给它分配一些资源。一个进程默认有一个线程,线程依附于进程,没有进程就没有线程。

多进程

  • 进程类

    Process([group [, target [, name [, args [, kwargs]]]]])
    
    group:指定进程组,目前只能使用None
    target:执行的目标任务名
    name:进程名字
    args:以元组方式给执行任务传参
    kwargs:以字典方式给执行任务传参
    
  • 使用格式

    import multiprocessing   #  导入进程模块
    进程变量名 = multiprocessing.Process(target = 目标任务名)  # 创建一个进程
    进程变量名.start()  # 开始这个进程
    
  • 示例:

    import multiprocessing
    import time
    
    def write():
        print('我在写作业')
    
    def see():
        print('我在看电视')
    
    def main():
        for i in range(5):
            time.sleep(0.5)
            process_write = multiprocessing.Process(target=write)
            process_see = multiprocessing.Process(target= see)
            process_write.start()
            process_see.start()
    
    if __name__ == '__main__':  # 验证是否是在当前文件调用
        main()
    

    运行结果:

    我在写作业
    我在看电视
    我在写作业
    我在看电视
    我在写作业
    我在看电视
    我在写作业
    我在看电视
    我在写作业
    我在看电视
    

获取进程号

​ 获取进程号可以用于确定子进程和其父进程之间的关系,能够知道子进程是由哪个父进程创建出来的。

获取当前进程号
  • os.getpid()可以获得当前运行的进程号,需要提前导入os模块

  • 格式:

    import os
    os.getpid()
    
  • 示例:

    import multiprocessing
    import time
    import os
    
    def write():
        for i in range(5):
            time.sleep(0.5)
            print('我在写作业',os.getpid())
    
    def see():
        for i in range(5):
            time.sleep(0.5)
            print('我在看电视',os.getpid())
    
    def main():
        process_write = multiprocessing.Process(target=write)
        process_see = multiprocessing.Process(target= see)
        process_write.start()
        process_see.start()
    
    if __name__ == '__main__':
        main()
    

    运行结果:

    我在写作业 10208
    我在看电视 7272
    我在写作业 10208
    我在看电视 7272
    我在写作业 10208
    我在看电视 7272
    我在写作业 10208
    我在看电视 7272
    我在写作业 10208
    我在看电视 7272
    
获取父进程号
  • os.getppid()可以获得当前运行的进程号,需要提前导入os模块

  • 格式:

    import os
    getppid()
    
  • 示例:

    import multiprocessing
    import time
    import os
    
    def write():
        for i in range(5):
            time.sleep(0.5)
            print('我在写作业,当前进程:%s,父进程:%s'%(os.getpid(),os.getppid()))
    
    def see():
        for i in range(5):
            time.sleep(0.5)
            print('我在看电视,当前进程:%s,父进程:%s'%(os.getpid(),os.getppid()))
    
    def main():
        print('我是父进程:',os.getpid())
        process_write = multiprocessing.Process(target=write)
        process_see = multiprocessing.Process(target= see)
        process_write.start()
        process_see.start()
    
    if __name__ == '__main__':
        main()
    

    运行结果:

    我是父进程: 2276
    我在写作业,当前进程:3604,父进程:2276
    我在看电视,当前进程:9572,父进程:2276
    我在看电视,当前进程:9572,父进程:2276
    我在写作业,当前进程:3604,父进程:2276
    我在看电视,当前进程:9572,父进程:2276
    我在写作业,当前进程:3604,父进程:2276
    我在写作业,当前进程:3604,父进程:2276
    我在看电视,当前进程:9572,父进程:2276
    我在看电视,当前进程:9572,父进程:2276
    我在写作业,当前进程:3604,父进程:2276
    

带参数传递的进程

args
  • Process类传递的args参数应和接收函数任务中的形参数量和顺序一致。

  • 以元组的形式传递参数

  • 示例:

    import multiprocessing
    import time
    
    def write(time_):
        print('我在写作业,想写%s分钟'%time_)
    
    def see(time_):
        print('我在看电视,想看%s分钟'%time_)
    
    def main():
        for i in range(5):
            time.sleep(0.5)
            process_write = multiprocessing.Process(target=write,args = (10,))
            process_see = multiprocessing.Process(target= see,args = (60,))
            process_write.start()
            process_see.start()
    
    if __name__ == '__main__':  # 验证是否是在当前文件调用
        main()
    

    运行结果:

    我在写作业,想写10分钟
    我在看电视,想看60分钟
    我在写作业,想写10分钟
    我在看电视,想看60分钟
    我在写作业,想写10分钟
    我在看电视,想看60分钟
    我在看电视,想看60分钟
    我在写作业,想写10分钟
    我在写作业,想写10分钟我在看电视,想看60分钟
    
kwargs
  • Process类传递的kwargs参数应和接收函数任务中的形参数量和名字一致。

  • 以字典的形式传递参数

  • 示例:

    import multiprocessing
    import time
    
    def write(time_,age):
        print('今年%s岁,我在写作业,想写%s分钟'%(age,time_))
    
    def see(age,time_):   # kwargs传递参数只要名字和数量一致就行,顺序没必要一致
        print('今年%s岁,我在看电视,想看%s分钟'%(age,time_))
    
    def main():
        for i in range(5):
            time.sleep(0.5)
            process_write = multiprocessing.Process(target=write,kwargs = {'time_':10,'age':17})
            process_see = multiprocessing.Process(target= see,kwargs = {'time_':10,'age':17})
            process_write.start()
            process_see.start()
    
    if __name__ == '__main__':  # 验证是否是在当前文件调用
        main()
    

    运行结果:

    今年17,我在写作业,想写10分钟
    今年17,我在看电视,想看10分钟
    今年17,我在看电视,想看10分钟今年17,我在写作业,想写10分钟
    
    今年17,我在看电视,想看10分钟
    今年17,我在写作业,想写10分钟
    今年17,我在写作业,想写10分钟
    今年17,我在看电视,想看10分钟
    今年17,我在写作业,想写10分钟
    今年17,我在看电视,想看10分钟
    

进程之间不共享全局变量

  • 创建的子进程会对主进程的资源进行拷贝,拷贝之后的资源是独立的。

  • 示例:

    import multiprocessing
    import time
    
    
    age = 17
    
    def write():
        global age
        for i in range(5):
            time.sleep(0.5)
            print('今年%d岁,我在写作业'%age)
    
    def see():
        global age
        for i in range(5):
            time.sleep(0.5)
            age += 1
            print('今年%d岁,我在看电视'%age)
    
    def main():
    
    
        process_write = multiprocessing.Process(target=write)
        process_see = multiprocessing.Process(target= see)
        process_write.start()
        process_see.start()
        time.sleep(5)
        print('主进程全局变量%d'%age)
    
    if __name__ == '__main__':  # 验证是否是在当前文件调用
        main()
    

    运行结果:

    今年18,我在看电视
    今年17,我在写作业
    今年17,我在写作业
    今年19,我在看电视
    今年17,我在写作业今年20,我在看电视
    
    今年21,我在看电视
    今年17,我在写作业
    今年22,我在看电视
    今年17,我在写作业
    主进程全局变量17
    

主进程会等待所有的子进程结束后再结束

  • 若想在主进程结束时同时结束子进程可以使用两种方式:

    • 守护进程
    • 销毁子进程
  • 示例:

    import multiprocessing
    import time
    
    def write():
    
        for i in range(5):
            time.sleep(0.5)
            print('子进程1还没结束')
    
    def see():
    
        for i in range(5):
            time.sleep(0.5)
            print('子进程2还没结束')
    
    def main():
    
    
        process_write = multiprocessing.Process(target=write)
        process_see = multiprocessing.Process(target= see)
        process_write.start()
        process_see.start()
    
    if __name__ == '__main__':  # 验证是否是在当前文件调用
        main()
        time.sleep(1)
        print('主进程结束')
    

    运行结果:

  子进程1还没结束
子进程2还没结束
  主进程结束
  子进程2还没结束
  子进程1还没结束
  子进程1还没结束
  子进程2还没结束
  子进程2还没结束
  子进程1还没结束
  子进程2还没结束子进程1还没结束
守护进程
  • 将Process类中的daemon参数设置为True可以实现结束主进程时终结子进程

  • 示例:

    import multiprocessing
    import time
    
    def write():
    
        for i in range(5):
            time.sleep(0.5)
            print('子进程1还没结束')
    
    def see():
    
        for i in range(5):
            time.sleep(0.5)
            print('子进程2还没结束')
    
    def main():
    
    
        process_write = multiprocessing.Process(target=write,daemon=True)
        process_see = multiprocessing.Process(target= see)
        process_write.start()
        process_see.daemon = True
        process_see.start()
    
    if __name__ == '__main__':  # 验证是否是在当前文件调用
        main()
        time.sleep(1)
        print('主进程结束')
    

    运行结果:

    子进程1还没结束
    子进程2还没结束
    主进程结束
    
销毁子进程
  • Process类中的terminate函数可以实现销毁子进程的功能

  • 示例:

    import multiprocessing
    import time
    
    def write():
    
        for i in range(5):
            time.sleep(0.5)
            print('子进程1还没结束')
    
    def see():
    
        for i in range(5):
            time.sleep(0.5)
            print('子进程2还没结束')
    
    
    
    if __name__ == '__main__':  # 验证是否是在当前文件调用
        process_write = multiprocessing.Process(target=write)
        process_see = multiprocessing.Process(target=see)
        process_write.start()
        process_see.start()
        time.sleep(1)
        print('主进程结束')
        process_write.terminate()
        process_see.terminate()
    
    

    运行结果:

    子进程1还没结束
    子进程2还没结束
    主进程结束
    

线程

线程的概念

​ 线程是进程中执行代码的一个分支,由cpu进行调度,线程是cpu调度的基本单位。每个进程都至少有一个线程,也就是主线程。

​ 多个线程可能由一个cpu执行,也可能由多个cpu执行。

​ python中的线程具有gil(全局解释器锁),cpu会争抢一个进程中的gil,只有抢到gil才可以进行线程代码。这个行为会限制多任务执行的性能,若想提升性能可以用多进程来执行。一个进程具有一个gil。

多线程

使用多线程首先需要导入多线程模块
import threading
线程类Thread参数说明
Thread([group [, target [, name [, args [, kwargs]]]]])

group: 线程组,目前只能使用None
target: 执行的目标任务名
args: 以元组的方式给执行任务传参
kwargs: 以字典方式给执行任务传参
name: 线程名,一般不用设置
格式:
import threading
def a():
    print('线程名:',threading.current_thread().name)
# threading.current_thread().name 是当前进程的名字
    for i in range(5):
        pass
a_thread = threading.Thread(target = a)  # 建立一个线程变量,也就是建立一个子线程
a_thread.start()   # 开始这个线程

多线程示例:
import threading
import time

def write():
    for i in range(10):
        print('我在写作业...%s'%i)
        time.sleep(0.5)

def see():
    for i in range(10):
        print('我在看电视...%s'%i)
        time.sleep(0.5)

write_thread = threading.Thread(target = write)
see_thread = threading.Thread(target= see)
write_thread.start()
see_thread.start()

运行结果:

我在写作业...0
我在看电视...0
我在写作业...1我在看电视...1

我在看电视...2
我在写作业...2
我在看电视...3我在写作业...3

我在写作业...4
我在看电视...4
我在看电视...5我在写作业...5

我在写作业...6
我在看电视...6
我在写作业...7我在看电视...7

我在写作业...8我在看电视...8

我在写作业...9我在看电视...9
带参数
  • args参数的使用: args传递的是一个元组参数,元组中的项应和函数中的形参一一对应。

    import threading
    import time
    
    def write(time_):
        for i in range(time_):
            print('我在写作业...%s'%i)
            time.sleep(0.5)
    
    def see(time_):
        for i in range(time_):
            print('我在看电视...%s'%i)
            time.sleep(0.5)
    
    time_ = int(input('请输入您想要的次数:'))
    write_thread = threading.Thread(target = write,args=(time_,))
    see_thread = threading.Thread(target= see,args=(time_,))
    write_thread.start()
    see_thread.start()
    

    运行结果:

    请输入您想要的次数:6
    我在写作业...0
    我在看电视...0
    我在看电视...1
    我在写作业...1
    我在写作业...2
    我在看电视...2
    我在写作业...3
    我在看电视...3
    我在看电视...4
    我在写作业...4
    我在写作业...5我在看电视...5
    
  • kwargs参数的使用:kwargs传递的是一个字典参数,字典中的key应和函数中的形参名字相同并且数量一致。

    import threading
    import time
    
    def write(time_):
        for i in range(time_):
            print('我在写作业...%s'%i)
            time.sleep(0.5)
    
    def see(time_):
        for i in range(time_):
            print('我在看电视...%s'%i)
            time.sleep(0.5)
    
    time_time = {}
    time_time['time_'] = int(input('请输入您想要的次数:'))
    write_thread = threading.Thread(target = write,kwargs = {'time_':time_time.get('time_')})
    see_thread = threading.Thread(target= see,kwargs = {'time_':time_time.get('time_')})
    write_thread.start()
    see_thread.start()
    

    运行结果:

    请输入您想要的次数:4
    我在写作业...0
    我在看电视...0
    我在写作业...1
    我在看电视...1
    我在写作业...2
    我在看电视...2
    我在写作业...3
    我在看电视...3
    

多个线程的执行是无序的

import threading
import time


def task():
    time.sleep(3)
    print("当前线程:", threading.current_thread().name)


if __name__ == '__main__':

   for _ in range(5):
       sub_thread = threading.Thread(target=task)
       sub_thread.start()

运行结果:

当前线程: Thread-3
当前线程: Thread-1
当前线程: Thread-2
当前线程: Thread-4
当前线程: Thread-5

主线程会等待所有子进程都结束后在结束

import threading
import time

def a():
    for i in range(5):
        print('子进程:%s'%threading.current_thread().name)
        time.sleep(1)

a_thread = threading.Thread(target = a)
a_thread.start()
time.sleep(2)
print('主进程结束',threading.current_thread().name)

运行结果:

import threading
import time

def a():
    for i in range(5):
        print('子进程:%s'%threading.current_thread().name)
        time.sleep(1)

a_thread = threading.Thread(target = a)
a_thread.start()
time.sleep(2)
print('主进程结束',threading.current_thread().name)

设置守护线程,使主线程结束时终结子线程

  • 设置Thread类中的参数daemon=True实现守护进程

    import threading
    import time
    def see():
        for i in range(10):
            print('看电视')
            time.sleep(0.5)
    thread_see = threading.Thread(target = see,daemon = True)
    thread_see.start()
    for i in range(5):
        print('吃饭')
        time.sleep(0.5)
    print('吃完饭了,不能看电视了')
    

    运行结果:

      看电视
      吃饭
      吃饭
      看电视
      吃饭
      看电视
      吃饭
      看电视
      吃饭
      看电视
      吃完饭了,不能看电视了
    
  • 引用Thread类中的方法setDeamon(True)

    import threading
    import time
    def see():
        for i in range(10):
            print('看电视')
            time.sleep(0.5)
    thread_see = threading.Thread(target = see)
    thread_see.setDaemon(True)
    thread_see.start()
    for i in range(5):
        print('吃饭')
        time.sleep(0.5)
    print('吃完饭了,不能看电视了')
    

    运行结果:

    看电视
    吃饭
    吃饭看电视
    
    吃饭
    看电视
    吃饭
    看电视
    吃饭
    看电视
    吃完饭了,不能看电视了
    

多个线程共享全局变量

import threading
list1 = []
def writelist():
    for i in range(10):
        list1.append(i)
    print('写完数据啦')
def readlist():
    print('开始读数据啦',list1)
    
w_thread = threading.Thread(target = writelist)
r_thread = threading.Thread(target = readlist)
w_thread.start()
r_thread.start()

运行结果:

写完数据啦
开始读数据啦 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 当进行大量数据处理时,由于多个线程共享全局变量,所以可能会造成处理结果出错。解决方法:不用多线程,线程等待,互斥锁。

    import threading
    
    g_num = 0
    def sum_num1():
        for i in range(1000000):
            global g_num
            g_num += 1
        print("sum1:", g_num)
    
    def sum_num2():
        for i in range(1000000):
            global g_num
            g_num += 1
        print("sum2:", g_num)
    
    if __name__ == '__main__':
        first_thread = threading.Thread(target=sum_num1)
        second_thread = threading.Thread(target=sum_num2)
        first_thread.start()
        second_thread.start()
    

    运行结果:

    sum1: 1295907
    sum2: 1783027
    # 计算结果并不是正确的20000000,这是由于g_num+=1在cpu中其实是分三个步骤执行,而进程的切换却可能会打断这三个步骤,使结果出错。
    

线程等待(join)

  • Thread类中的join函数可以实现线程等待的作用,即一个线程执行结束后才会继续向下执行代码。

    import threading
    
    g_num = 0
    def sum_num1():
        for i in range(1000000):
            global g_num
            g_num += 1
        print("sum1:", g_num)
    
    def sum_num2():
        for i in range(1000000):
            global g_num
            g_num += 1
        print("sum2:", g_num)
    
    if __name__ == '__main__':
        first_thread = threading.Thread(target=sum_num1)
        second_thread = threading.Thread(target=sum_num2)
        first_thread.start()
        first_thread.join()
        second_thread.start()
    

互斥锁

  • Thread类中的Lock()函数可以实现互斥锁的功能。

  • 互斥锁可以对共享数据进行锁定,保证同一时刻只有一个线程操作共享数据。

  • 互斥锁由多个线程争抢,抢到的执行,未抢到的等待。

  • 使用方法:

    import threading
    互斥锁变量 = threading.lock() # 创建互斥锁 
    互斥锁变量.acquire() # 上锁
    互斥锁变量.release() # 解锁
    
  • 示例:

    import threading
    
    g_num = 0
    g_Lock = threading.Lock()
    def sum_num1():
        for i in range(1000000):
            global g_num
            g_Lock.acquire()
            g_num += 1
            g_Lock.release()
        print("sum1:", g_num)
    
    def sum_num2():
        for i in range(1000000):
            global g_num
            g_Lock.acquire()
            g_num += 1
            g_Lock.release()
        print("sum2:", g_num)
    
    if __name__ == '__main__':
        first_thread = threading.Thread(target=sum_num1)
        second_thread = threading.Thread(target=sum_num2)
        first_thread.start()
        second_thread.start()
    

    运行结果:

    sum1: 1680016
    sum2: 2000000
    
  • 死锁

    • 当互斥锁使用不当时就会出现死锁,类似于死循环,妨碍程序正常运行。

    • 示例:

      import threading
      
      g_num = 0
      g_Lock = threading.Lock()
      def sum_num1():
          for i in range(1000000):
              global g_num
              g_Lock.acquire()
              g_num += 1
              return
              g_Lock.release()
          print("sum1:", g_num)
      
      def sum_num2():
          for i in range(1000000):
              global g_num
              g_Lock.acquire()
              g_num += 1
              g_Lock.release()
          print("sum2:", g_num)
      
      if __name__ == '__main__':
          first_thread = threading.Thread(target=sum_num1)
          second_thread = threading.Thread(target=sum_num2)
          first_thread.start()
          second_thread.start()
      

      运行结果:

      # 无结果,且程序一直未结束
      

线程和进程对比

  1. 进程之间不共享全局变量
  2. 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
  3. 创建进程的资源开销要比创建线程的资源开销要大
  4. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
  5. 线程不能够独立执行,必须依存在进程中
  6. 多进程开发比单进程多线程开发稳定性要强

协程

协程的概念

​ 由于python中的线程存在gil,所以python开发了协程来实现进程中多任务的切换。(进程包含线程包含协程)

​ 协程所消耗的资源小于线程,更小于进程。协程的切换顺序由程序员自己控制,通过代码实现。

​ 协程使用时会创建一个协程管理表,依照协程的创建顺序来执行协程,当一个协程遇到一些耗时操作时(列如输入输出操作),会切换到下一个协程来执行。

​ 进程不共享全局变量,是因为子进程会复制主进程的资源。每个进程至少有一个线程,线程共享全局变量。协程存在于线程中,所以也共享全局变量。

协程的使用方式

  • python类通过协程模块gevent来实现协程

  • 当创建一个协程时,该协程自动开始。

  • 由于gevent协程是后加的,不在线程的正常使用规则内。所以主线程结束时不会等待协程执行结束,这时就需要通过代码使主线程等待协程结束再结束。

  • gevent无法认知耗时(time.sleep()),所以当需要使用耗时操作时需要引入gevent模块中的monkey类,将耗时代码更改为gevent本身的耗时操作。

  • 协程的标准使用格式

    import gevent                   # 导入gevent模块
    from gevent import monkey		# 导入gevent模块中的monkey类
    monkey.patch_all()              # 自动将一些gevent不认识的操作替换为gevent本身的操作
    协程变量= gevent.spawn(任务名)	    # 创建一个协程,当创建协程时自动开始此协程
    gevent.joinall(协程变量)          # 使主线程等待协程结束再结束
    
  • 示例:

    import gevent
    import time
    from gevent import monkey
    
    monkey.patch_all()
    
    def write_chinese():
        for i in range(10):
            time.sleep(0.5)
            print('写语文作业')
    
    def write_english():
        for i in range(10):
            time.sleep(0.5)
            print('写英语作业')
    
    ch=gevent.spawn(write_chinese)
    en=gevent.spawn(write_english)
    time.sleep(2)
    print('开始等待协程')
    gevent.joinall((ch,en))
    print('主线程执行结束')
    
  
  运行结果:
  
  ```python
  写语文作业
  写英语作业
  写语文作业
  写英语作业
  写语文作业
  写英语作业
  开始等待协程
  写语文作业
  写英语作业
  写语文作业
  写英语作业
  写语文作业
  写英语作业
  写语文作业
  写英语作业
  写语文作业
  写英语作业
  写语文作业
写英语作业
  写语文作业
  写英语作业
  主线程执行结束
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值