进程和线程

进程是系统进行资源分配的最小单位,线程是系统进行调度执行的最小单位;

一个应用程序至少包含一个进程,一个进程至少包含一个线程;

每个进程在执行过程中拥有独立的内存空间,而一个进程中的线程之间是共享该进程的内存空间的;

  • 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
  • 假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。编者注: 多核的CPU就像有了多个发电厂,使多工厂(多进程)实现可能。
  • 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
  • 一个车间里,可以有很多工人。他们协同完成一个任务。
  • 线程就好比车间里的工人。一个进程可以包括多个线程。
  • 车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
  • 可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
  • 一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
  • 还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
  • 这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。
  • 不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。

Python的多进程

Python的多进程依赖于multiprocess模块;使用多进程可以利用多个CPU进行并行计算;

实例:

from multiprocessing import Process
import os
import time def long_time_task(i):
print('子进程: {} - 任务{}'.format(os.getpid(), i))
time.sleep(2)
print("结果: {}".format(8 ** 20)) if __name__=='__main__':
print('当前母进程: {}'.format(os.getpid()))
start = time.time()
p1 = Process(target=long_time_task, args=(1,))
p2 = Process(target=long_time_task, args=(2,))
print('等待所有子进程完成。')
p1.start()
p2.start()
p1.join()
p2.join()
end = time.time()
print("总共用时{}秒".format((end - start)))

新创建进程和进程间切换是需要消耗资源的,所以应该控制进程数量;

同时可运行的进程数量收到CPU核数限制;

进程池

使用进程池pool创建进程:

使用进程池可以避免手工进行进程的创建的麻烦,默认数量是CPU核数;

Pool类可以提供指定数量的进程供用户使用,当有新的请求被提交到Pool中的时候,如果进程池还没有满,就会创建一个新的进程来执行请求;如果池已经满了,请求就会等待,等到有空闲进程可以使用时,才会执行请求;

几个方法:

1.apply_async

作用是向进程池提交需要执行的函数和参数,各个进程采用非阻塞的异步方式调用,每个进程只管自己运行,是默认方式;

2.map

会阻塞进程直到返回结果;

3.map_sunc

非阻塞进程;

4.close

关闭进程池,不再接受任务;

5.terminate

结束进程;

6.join

主进程阻塞,直到子进程执行结束;

实例:

from multiprocessing import Pool, cpu_count
import os
import time def long_time_task(i):
print('子进程: {} - 任务{}'.format(os.getpid(), i))
time.sleep(2)
print("结果: {}".format(8 ** 20)) if __name__=='__main__':
print("CPU内核数:{}".format(cpu_count()))
print('当前母进程: {}'.format(os.getpid()))
start = time.time()
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('等待所有子进程完成。')
p.close()
p.join()
end = time.time()
print("总共用时{}秒".format((end - start)))

在join之前,必须使用close或者terminate,让进程池不再接受任务;

多进程间的数据通信与共享

通常,进程之间是相互独立的,每个进程都有独立的内存。通过共享内存(nmap模块),进程之间可以共享对象,使多个进程可以访问同一个变量(地址相同,变量名可能不同)。多进程共享资源必然会导致进程间相互竞争,所以应该尽最大可能防止使用共享状态。还有一种方式就是使用队列queue来实现不同进程间的通信或数据共享,这一点和多线程编程类似。

下例这段代码中中创建了2个独立进程,一个负责写(pw), 一个负责读(pr), 实现了共享一个队列queue。

from multiprocessing import Process, Queue
import os, time, random # 写数据进程执行的代码:
def write(q):
print('Process to write: {}'.format(os.getpid()))
for value in ['A', 'B', 'C']:
print('Put %s to queue...' % value)
q.put(value)
time.sleep(random.random()) # 读数据进程执行的代码:
def read(q):
print('Process to read:{}'.format(os.getpid()))
while True:
value = q.get(True)
print('Get %s from queue.' % value) if __name__=='__main__':
# 父进程创建Queue,并传给各个子进程:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 启动子进程pr,读取:
pr.start()
# 等待pw结束:
pw.join()
# pr进程里是死循环,无法等待其结束,只能强行终止:
pr.terminate()

Python的多线程

python 3中的多进程编程主要依靠threading模块。创建新线程与创建新进程的方法非常类似。threading.Thread方法可以接收两个参数, 第一个是target,一般指向函数名,第二个时args,需要向函数传递的参数。对于创建的新线程,调用start()方法即可让其开始。我们还可以使用current_thread().name打印出当前线程的名字。

import threading
import time def long_time_task(i):
print('当前子线程: {} 任务{}'.format(threading.current_thread().name, i))
time.sleep(2)
print("结果: {}".format(8 ** 20)) if __name__=='__main__':
start = time.time()
print('这是主线程:{}'.format(threading.current_thread().name))
thread_list = []
for i in range(1, 3):
t = threading.Thread(target=long_time_task, args=(i, ))
thread_list.append(t)
for t in thread_list:
t.start()
for t in thread_list:
t.join()
end = time.time()
print("总共用时{}秒".format((end - start)))

多线程间的数据共享

一个进程所含的不同线程间共享内存,这就意味着任何一个变量都可以被任何一个线程修改,因此线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。如果不同线程间有共享的变量,其中一个方法就是在修改前给其上一把锁lock,确保一次只有一个线程能修改它。threading.lock()方法可以轻易实现对一个共享变量的锁定,修改完后release供其它线程使用。

import threading

class Account:
def __init__(self):
self.balance = 0 def add(self, lock):
# 获得锁
lock.acquire()
for i in range(0, 100000):
self.balance += 1
# 释放锁
lock.release() def delete(self, lock):
# 获得锁
lock.acquire()
for i in range(0, 100000):
self.balance -= 1
# 释放锁
lock.release() if __name__ == "__main__":
account = Account()
lock = threading.Lock()
# 创建线程
thread_add = threading.Thread(target=account.add, args=(lock,), name='Add')
thread_delete = threading.Thread(target=account.delete, args=(lock,), name='Delete') # 启动线程
thread_add.start()
thread_delete.start() # 等待线程结束
thread_add.join()
thread_delete.join() print('The final balance is: {}'.format(account.balance))

使用queue队列通信-经典的生产者和消费者模型

from queue import Queue
import random, threading, time # 生产者类
class Producer(threading.Thread):
def __init__(self, name, queue):
threading.Thread.__init__(self, name=name)
self.queue = queue def run(self):
for i in range(1, 5):
print("{} is producing {} to the queue!".format(self.getName(), i))
self.queue.put(i)
time.sleep(random.randrange(10) / 5)
print("%s finished!" % self.getName()) # 消费者类
class Consumer(threading.Thread):
def __init__(self, name, queue):
threading.Thread.__init__(self, name=name)
self.queue = queue def run(self):
for i in range(1, 5):
val = self.queue.get()
print("{} is consuming {} in the queue.".format(self.getName(), val))
time.sleep(random.randrange(10))
print("%s finished!" % self.getName()) def main():
queue = Queue()
producer = Producer('Producer', queue)
consumer = Consumer('Consumer', queue) producer.start()
consumer.start() producer.join()
consumer.join()
print('All threads finished!') if __name__ == '__main__':
main()
  • 对CPU密集型代码(比如循环计算) - 多进程效率更高
  • 对IO密集型代码(比如文件操作,网络爬虫) - 多线程效率更高。

对于IO密集型操作,大部分消耗时间其实是等待时间,在等待时间中CPU是不需要工作的,那你在此期间提供双CPU资源也是利用不上的,相反对于CPU密集型代码,2个CPU干活肯定比一个CPU快很多。那么为什么多线程会对IO密集型代码有用呢?这时因为python碰到等待会释放GIL供新的线程使用,实现了线程间的切换。

Python的多进程和多线程的更多相关文章

  1. Python 中多进程、多线程、协程

    进程: 一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享.开销大. 线程: 调度执行的最小单位,也叫执行路径,不 ...

  2. python之多进程and多线程

    图文来自互联网 一.什么是进程和线程 (https://jq.qq.com/?_wv=1027&k=rX9CWKg4) 进程是分配资源的最小单位,线程是系统调度的最小单位. 当应用程序运行时最 ...

  3. Python之多进程和多线程

    目标: 1.os.fork简单示例 2.使用os.fork多进程测试IP是否在线 3.使用os.fork多进程解决tcpserver多客户端连接问题 4.多线程测试IP地址是否在线 1.os.fork ...

  4. 【python】多进程、多线程、序列

    一.多进程 1.子进程永远返回0,而父进程返回子进程的ID.这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程 ...

  5. 【转】Python中的GIL、多进程和多线程

    转自:http://lesliezhu.github.io/public/2015-04-20-python-multi-process-thread.html 目录 1. GIL(Global In ...

  6. Python中的多进程与多线程(二)

    在上一章中,学习了Python多进程编程的一些基本方法:使用跨平台多进程模块multiprocessing提供的Process.Pool.Queue.Lock.Pipe等类,实现子进程创建.进程池(批 ...

  7. python采用 多进程/多线程/协程 写爬虫以及性能对比,牛逼的分分钟就将一个网站爬下来!

    首先我们来了解下python中的进程,线程以及协程! 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务.一个CPU,在一个时间切片里只能运行一个程序. 从操作系统的角度: 进程和线程,都 ...

  8. 在python中单线程,多线程,多进程对CPU的利用率实测以及GIL原理分析

    首先关于在python中单线程,多线程,多进程对cpu的利用率实测如下: 单线程,多线程,多进程测试代码使用死循环. 1)单线程: 2)多线程: 3)多进程: 查看cpu使用效率: 开始观察分别执行时 ...

  9. 学习笔记--python中使用多进程、多线程加速文本预处理

    一.任务描述 最近尝试自行构建skip-gram模型训练word2vec词向量表.其中有一步需要统计各词汇的出现频率,截取出现频率最高的10000个词汇进行保留,形成常用词词典.对于这个问题,我建立了 ...

随机推荐

  1. shit LeetCode interview Question

    shit LeetCode interview Question https://leetcode.com/interview/1/ 有点晕,啥意思,没太明白,到底是要按什么排序呀? 去掉 标识符 不 ...

  2. codepen iframe theme id

    codepen iframe theme id iframe css theme demos See the Pen css margin collapsing (1. 相邻兄弟元素) by xgqf ...

  3. CSS margin collapsing All In One

    CSS margin collapsing All In One margin collapsing margin 塌陷 / margin 合并 会出现 margin collapsing 的 3 种 ...

  4. js online playground & web editor

    js online playground & web editor -javascript playgrounds 2019 https://scotch.io/tutorials/7-jav ...

  5. ES6 & import * & import default & import JSON

    ES6 & import * & import default & import JSON import json & default value bug api.js ...

  6. eui & search select

    eui & search select https://element.eleme.io/#/zh-CN/component/select demo <template> < ...

  7. Dart 中断Future

    更多 中断future 方法1) import 'package:async/async.dart'; void main() { var get = CancelableOperation.from ...

  8. betterscroll上拉加载

    mounted(){ var that=this; setTimeout(()=>{ let scroll = new BScroll(document.getElementById('wrap ...

  9. 「NGK每日快讯」2021.1.4日NGK第62期官方快讯!

  10. 最佳搭档:利用 SSH 及其配置文件节省你的生命

    本文转载自最佳搭档:利用 SSH 及其配置文件节省你的生命 导语 SSH 协议是事实上的互联网基石之一.在 SSH 协议出现之前(1995 年由 Tatu Ylonen 设计),通过互联网远程登录其他 ...