什么是进程、线程

  进程:一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等,对各种资源管理的基本单位。

  线程:操作系统最小的调度单位, 是一串指令的集合,进程中的一个执行单元。

  一个进程至少有一个线程。

  全局解释器锁(GIL):python虚拟机的访问由全局解释器锁控制,这个锁能保证同一时刻只有一个线程运行。

进程与线程的区别

  ◐ 线程之间共享内存空间,而进程的内存是独立,即使是父子进程
  ◐ 同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现
  ◐ 创建新线程很简单,但是创建一个新进程需要克隆一次父进程
  ◐ 一个线程可以控制和操作同一个进程内的其他线程,而进程只能操作子进程
 

多线程环境中,python虚拟机的执行方式

  (1)设置GIL
  (2)切换到一个线程运行
  (3)运行指定数量的指令或者线程主动让开控制
  (4)把线程设置为睡眠状态
  (5)解锁GIL
  (6)从头再来执行其他的线程
 

Part1 简单的线程

 import threading
import time def test(n):
print("thread",n)
time.sleep(3) t1 = threading.Thread(target=test,args=("tt1",)) # 启动一个线程t1,执行test函数,参数为字符串tt1
t2 = threading.Thread(target=test,args=("tt2",))
t1.start() # 使用两个线程执行这个函数,cpu在t1执行完print后,遇到sleep,就会切换到t2执行print
t2.start() # test("tt1") # 而直接调用两个函数执行,cpu会先执行完第一个,再执行下一个,这样比多线程多了个执行sleep的时间
# test("tt2")

Part2 用类的形式启动线程

 # 用类的形式启动线程
import threading class MyThread(threading.Thread):
def __init__(self,n):
super(MyThread,self).__init__()
self.n = n def run(self): # 这里必须是run函数,不能取其他名,里面写死了会调用run函数
print("run thread",self.n) t1 = MyThread("tt1")
t2 = MyThread("tt2")
t1.start()
t2.start()

Part3 主线程等待子线程执行完成

 import threading
import time def run(n):
print("th:",n,threading.current_thread()) # 可以查看当前进程是为主线程还是子线程
time.sleep(2) start_time = time.time()
t_list = [] # 定义一个空列表,用来存启动的子线程
for i in range(50):
t = threading.Thread(target=run,args=("t-%d" %i,))
t.start()
t_list.append(t)
# print("cost time:",time.time()-start_time)
# 按目前所知可得,这里打印的时间是整个程序执行一共花的时间。但是最终执行下来时间只有0.02左右,里面怎么没有包含函数sleep的时间呢
# 一个进程至少有一个线程,从执行这个程序开始,就启动了一个主线程,而主线程中启动了50个子线程,而子线程启动后和主线程独立没有影响(主子并行)
# 其中的print语句也是主线程部分,sleep语句是子线程部分,所以打印的时间是主线程启动了50个子线程的时间,主线程并不会等待子线程执行完了再执行后面的程序
print(threading.active_count()) # 输出51,表示当前存活的线程,包含主线程
for t in t_list:
t.join() print("cost time:",time.time()-start_time,threading.current_thread())
# 这里就使用join来等待子线程的完成,其中等待是指主线程等待子线程执行完后再继续执行,默认程序最后都有一个join
# 不能直接在启动线程的循环里写join,那样会变成串行,因为每启动一个线程,都要等待执行完成后才启动下一个线程
# 这里直接循环每个已经启动了的线程,主线程会等所有的子线程执行完后再执行print时间

Part4 守护线程

 # 守护线程,主线程执行完了,不管守护线程有没有执行完都退出
import threading
import time def run(n):
print("th:",n)
time.sleep(2) start_time = time.time()
for i in range(50):
t = threading.Thread(target=run,args=("t%d" %i,))
t.setDaemon(True) # 把当前线程设置为守护线程,必须在start之前设置
t.start()
print("cost time:",time.time()-start_time)
# 主线程不是守护线程,程序会等主线程执行完之后,不会等待守护线程,也就是子线程,就直接程序退出了

Part5 使用全局解释器锁

 # 设置全局解释器锁
import threading
import time def run(n):
lock.acquire() # 设置锁
print("th:",n)
global num
num += 1
lock.release() # 释放锁
time.sleep(2) lock = threading.Lock() # 生成一个锁
num = 0
start_time = time.time()
for i in range(50):
t = threading.Thread(target=run,args=("t%d" %i,))
t.start()
print(num)
print("cost time:",time.time()-start_time)

Part6 使用递归锁

 import threading, time

 def run1():
print("grab the first part data")
lock.acquire()
global num
num += 1
lock.release()
return num def run2():
print("grab the second part data")
lock.acquire()
global num2
num2 += 1
lock.release()
return num2 def run3():
lock.acquire()
res = run1()
print('--------between run1 and run2-----')
res2 = run2()
lock.release()
print(res, res2) num, num2 = 0, 0
lock = threading.RLock() # 定义递归锁
for i in range(10):
t = threading.Thread(target=run3)
t.start() while threading.active_count() != 1:
print(threading.active_count())
else:
print('----all threads done---')
print(num, num2)

Part7 信号量

 # 信号量 一般用于连接池,并发数
import threading, time def run(n):
semaphore.acquire()
time.sleep(1)
print("run the thread: %s\n" % n)
semaphore.release() if __name__ == '__main__':
semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行,并非5个执行完了再执行下5个,是保持在5个
for i in range(22):
t = threading.Thread(target=run, args=(i,))
t.start()
while threading.active_count() != 1:
pass # print threading.active_count()
else:
print('----all threads done---')

Part8 队列queue

 import queue

 q = queue.Queue()  # 实例化队列,数据先入先出
# q = queue.Queue(maxsize=3) 最多存放3个数据,put第四个时候就会卡住,等数据有被取走,就放进去
# q = queue.LifoQueue() 数据后入先出
# q = queue.PriorityQueue() 设置优先级 q.put("a") # 存入数据
q.put(123)
#q.put("a",block=False) # 放进数据超过指定最大数量就会报异常
#q.put("a",timeout=3) # q满了,等待3秒还是不能放进去的话就报错
#q.put((2,"p1")) 传入元组,第一个元素是优先级,从小到大取数据
#q.put((-1,"p1"))
#q.put((6,"p1")) print(q.qsize()) # 返回队列里元素数量
print(q.get()) # 获取一个数据,如果队列里没有数据就会卡住
#q.get(timeout=3) # 有数据就立刻获取返回,如果没有数据就等待3秒,若依然没有数据就报异常
#q.get(block=False) # 如果队列里没有数据就会报异常,默认为True
#q.get_nowait() # 如果队列里没有数据就会报异常

Part9 生产消费模型

 import threading
import time
import queue q = queue.Queue(maxsize=10) def productData(name):
i = 1
while True:
time.sleep(0.4)
q.put("数据%s" %i)
print("[%s] 生产了 数据[%s]" %(name,i))
i += 1 def consumeData(name):
while True:
print("[%s] 消费了 [%s]" %(name,q.get()))
time.sleep(1) p = threading.Thread(target=productData,args=("p1",))
c1 = threading.Thread(target=consumeData,args=("c1",))
c2 = threading.Thread(target=consumeData,args=("c2",))
p.start()
c1.start()
c2.start()

python入门之进程与线程的更多相关文章

  1. python中的进程、线程(threading、multiprocessing、Queue、subprocess)

    Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...

  2. python并发编程-进程池线程池-协程-I/O模型-04

    目录 进程池线程池的使用***** 进程池/线程池的创建和提交回调 验证复用池子里的线程或进程 异步回调机制 通过闭包给回调函数添加额外参数(扩展) 协程*** 概念回顾(协程这里再理一下) 如何实现 ...

  3. Python之路-python(paramiko,进程和线程的区别,GIL全局解释器锁,线程)

    一.paramiko 二.进程.与线程区别 三.python GIL全局解释器锁 四.线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生 ...

  4. Python 中的进程、线程、协程、同步、异步、回调

    进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生? 一.上下文切换技术 简述 在进一步之前,让我们先回顾一下各种上下文切换技术. 不过首先说 ...

  5. Python学习--17 进程和线程

    线程是最小的执行单元,而进程由至少一个线程组成.如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间. 进程 fork调用 通过fork()系统调用,就可以生成一个子进程 ...

  6. Python自学笔记-进程,线程(Mr serven)

    对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了 ...

  7. Python之路,进程、线程、协程篇

      本节内容 进程.与线程区别 cpu运行原理 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...

  8. Python自动化开发 - 进程、线程(一)

    本节内容 1.操作系统发展史 2.进程和线程 3.Python threading 模块 一.操系统发展史 手工操作(无操作系统) 1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统 ...

  9. Python学习--18 进程和线程

    线程是最小的执行单元,而进程由至少一个线程组成.如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间. 进程 fork调用 通过fork()系统调用,就可以生成一个子进程 ...

随机推荐

  1. Cloudera运维

    1. 增加一个节点 1. 拷贝cm的jar包到该节点 2. 设置hostname(hostnamectl set-hostname XXX),然后修改hosts文件 3. 所有的节点添加该hostna ...

  2. DOM基础知识整理

    --<JavaScript高级程序设计>Chapter10学习笔记 一.节点层次 1.Node类型 1)(Node).nodeType属性 节点nodeType属性有12种. 检测node ...

  3. keepalive安装和配置

    1.下载安装包并解压 sudo wget http://www.keepalived.org/software/keepalived-1.2.13.tar.gz tar zxvf keepalived ...

  4. HDOJ2043(JAVAset容器练习)

    import java.io.PrintWriter; import java.util.HashSet; import java.util.Scanner; public class Main { ...

  5. JVM体系结构之六:堆Heap之2:新生代及新生代里的两个Survivor区(下一轮S0与S1交换角色,如此循环往复)、常见调优参数

    一.为什么会有年轻代 我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能.你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我 ...

  6. JavaScript-Tool:my97datepicker

    ylbtech-JavaScript-Tool:my97datepicker 1.返回顶部 1. 2.下载 https://files.cnblogs.com/files/storebook/java ...

  7. ES6学习之Generator函数

    概念:可以把Generator 函数理解成状态机(封装了多个内部状态)或者是一个遍历器对象生成函数 写法:Generator函数的定义跟普通函数差不多,只是在function关键字后面加了一个星号 f ...

  8. 忘记mysql密码,但是可以用navicat修改MySQL密码

    1.首先:要知道你的账户 2.打开可以连接的navicat,在查询语句页面,输入以下操作,就可以修改成功密码了

  9. Wix 使用总结(续)--关于Feature和Component的状态判断安装过程(转)

    安装过程中,有时候需要根据用户的设置来进行不同的安装,其中一个方面就是根据用户选择安装的Feature或者Component,来判断下一步的操作.    Wix中提供了相关的判断方法和内置的状态值.  ...

  10. Rreplication 性能差(转储200万门诊处方zjysb012)

    ETLDB性能差(HIS转储200万门诊处方zjysb012) 解决方法: 1.禁用cdc.Hismz_capture 2.停止cdc.Hismz_capture 3.关闭zjysb012,zjysb ...