python系统编程

1.进程

1.1进程的创建-fork
1.1.1进程 与 程序
  • 编写完毕的代码,在没有运行的时候,称之为程序
  • 正在运行着的代码,就成为进程
1.1.2 fork()
 import os

    # 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以
    pid = os.fork()

    if pid == 0:
        print('哈哈1')
    else:
        print('哈哈2')

说明:

  • 程序执行到os.fork()时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中
  • 然后父进程和子进程都会从fork()函数中得到一个返回值,在子进程中这个值一定是0,而父进程中是子进程的 id号
1.1.3 getpid()、getppid()
import os

rpid = os.fork()
if rpid<0:
    print("fork调用失败。")
elif rpid == 0:
    print("我是子进程(%s),我的父进程是(%s)"%(os.getpid(),os.getppid()))
    x+=1
else:
    print("我是父进程(%s),我的子进程是(%s)"%(os.getpid(),rpid))

print("父子进程都可以执行这里的代码")

运行结果:

我是父进程(19360),我的子进程是(19361)
父子进程都可以执行这里的代码
我是子进程(19361),我的父进程是(19360)
父子进程都可以执行这里的代码
1.1.4 多进程修改全局变量
#coding=utf-8
import os
import time

num = 0

# 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以
pid = os.fork()

if pid == 0:
    num+=1
    print('哈哈1---num=%d'%num)
else:
    time.sleep(1)
    num+=1
    print('哈哈2---num=%d'%num)
总结

多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响

Process语法结构如下:
Process([group [, target [, name [, args [, kwargs]]]]])
  • target:表示这个进程实例所调用对象;
  • args:表示调用对象的位置参数元组;
  • kwargs:表示调用对象的关键字参数字典;
  • name:为当前进程实例的别名;
  • group:大多数情况下用不到;
Process类常用方法:
  • is_alive():判断进程实例是否还在执行;
  • join([timeout]):是否等待进程实例执行结束,或等待多少秒;
  • start():启动进程实例(创建子进程);
  • run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法;
  • terminate():不管任务是否完成,立即终止;
Process类常用属性:
  • name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数;
  • pid:当前进程实例的PID值;
1.1.5 进程池Pool

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行,请看下面的实例:

multiprocessing.Pool常用函数解析:
  • apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给 func的关键字参数列表;
  • apply(func[, args[, kwds]]):使用阻塞方式调用func
  • close():关闭Pool,使其不再接受新的任务;
  • terminate():不管任务是否完成,立即终止;
  • join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
1.1.6进程间通信-Queue

初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头);

  • Queue.qsize():返回当前队列包含的消息数量;
  • Queue.empty():如果队列为空,返回True,反之False ;
  • Queue.full():如果队列满了,返回True,反之False;
  • Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;
    1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;
    2)如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常;
    Queue.get_nowait():相当Queue.get(False);
    Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True;
    1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常;
    2)如果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常;

2. 线程

多线程执行

import threading
import time

def saySorry():
    print("亲爱的,我错了,我能吃饭了吗?")
    time.sleep(1)

if __name__ == "__main__":
    for i in range(5):
        t = threading.Thread(target=saySorry)
        t.start() #启动线程,即让线程开始执行

2.1 线程的执行顺序

总结

1.每个线程一定会有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。
2.当线程的run()方法结束时该线程完成。
3.无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
4.线程的几种状态


image.png

2.2 多线程-共享全局变量

总结:

  • 在一个进程内的所有线程共享全局变量,能够在不适用其他方式的前提下完成多线程之间的数据共享(这点要比多进程要好)
  • 缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

2.2 进程VS线程

  • 进程,能够完成多任务,比如 在一台电脑上能够同时运行多个QQ
  • 线程,能够完成多任务,比如 一个QQ中的多个聊天窗口
    定义不同

进程是系统进行资源分配和调度的一个独立单位.

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

2.3 什么是同步

同步就是协同步调,按预定的先后次序进行运行。

2.4 互斥锁

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
互斥锁为资源引入一个状态:锁定/非锁定。
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
threading模块中定义了Lock类,可以方便的处理锁定:

#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([blocking])
#释放
mutex.release()

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

  • 如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为止(如果没有指定,那么默认为True)
  • 如果设定blocking为False,则当前线程不会堵塞

多线程-非共享数据

在多线程开发中,全局变量是多个线程都共享的数据,而局部变量等是各自线程的,是非共享的

2.5 死锁

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

避免死锁的方法

  • 程序设计时要尽量避免(银行家算法)
  • 添加超时时间等
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容