Day 41 线程
进程只能在同一个时间干一件事情,如果想同时干两件或者多件事情,进程就无能为力了。
进程在执行过程中如果阻塞,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行.
一是由于进程是资源的拥有者,创建和撤销和切换存在较大的时空开销,因此需要引入轻型进程,
因此在80年代,出现了能独立运行的基本单位---线程(Threads)
注意:进程是资源分配的最小单位, 线程是CPU 调度的最小单位。
每个进程中至少有一个线程。
线程与进程的区别以下区别4点
1. 地址空间和其他资源;进程间互相独立,同一进程的各线程间共享,某进程内的线程在其他进程不可见.
2 .通信,进程间通信IPC ,线程间可以直接读写进程数据段来进行通信。
3.调度和切换,线程上下文切换比进程上下文切换要快.
4. 在多线程操作系统中,进程不是一个可执行的实体.
在多线程操作系统中,通常是在一个进程中包括多个线程,每个线程都是作为cpu的基本单位,是花费最小开销的实体
线程具有以下属性:
1. 轻型实体 。
线程的实体包括程序,数据和tcb ,线程是动态概念,他的动态特性由线程控制块TCB 描述。
2. 独立调度和分派的基本单位.
3. 共享进程资源.(不需要进行IPC间通信了)
线程在同一进程中的各个线程,都可以共享该进程所有的资源,这表现在所有线程都具有相同的进程ID,这意味着线程可以访问该进程的每一个内存资源.由于同一个进程内的线程共享内存和文件,所以线程之间相互通信不必调用内核.
4 .可并发执行
在一个进程中的多个线程之间,可以并发执行,甚至运行在一个进程中所有线程都能并发执行,同样,不通进程中的线程也能并发执行,充分利用和发挥了处理机与外围并行工作的能力。
不同的进程之间是充满敌意的,彼此是抢占、竞争cpu的关系,如果迅雷会和QQ抢资源。而同一个进程是由一个程序员的程序创建,所以同一进程内的线程是合作关系,一个线程可以访问另外一个线程的内存地址,大家都是共享的。
1. 父进程有多个线程,那么开启的子线程是否需要同样多的线程
2. 在同一个进程中,如果一个线程关闭了文件,而另外一个线程正准备往该文件内写内容呢?
因此,在多线程的代码中,需要更多的心思来设计程序的逻辑、保护程序的数据。
全局解释器锁GIL
全局解释器锁-GIL 同一时刻只能有一个线程访问CPU.锁的是线程.
python解释器是python语言的问题吗 ?
是cpython解释器的特性. 这是python的一个弊病. 也是所有解释型语言的弊端.
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程环境中,Python 虚拟机按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。
在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。
线程的创建 Threading.Thread类
一 、创建线程的方式一
from threading import Thread
import time
def sayhi(name):
time.sleep()
print('%s say hello' %name) if __name__ == '__main__':
t=Thread(target=sayhi,args=('egon',))
t.start()
print('主线程')
输出结果:
二、创建线程的方式二
from threading import Thread
import time
class sayhi(Thread):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
time.sleep()
print('%s say hello' %self.name) if __name__ =='__main__':
t =sayhi('egon')
t.start()
print('主线程')
输出结果
多线程与多进程 PID的比较
from threading import Thread
from multiprocessing import Process
import os
def work():
print('hello',os.getpid()) if __name__ == '__main__':
# part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样.
t1 =Thread(target=work)
t2 =Thread(target=work)
t1.start()
t2.start()
print('主线程pid',os.getpid())
print()
#part2 开启多个进程,每个进程有不通的pid
p1 =Process(target=work)
p2 =Process(target=work)
p1.start()
p2.start()
print("主进程pid",os.getpid())
输出结果:
开发效率的比较
from threading import Thread
from multiprocessing import Process
import os def work():
print('hello') if __name__ == '__main__':
#在主进程下开启线程
t=Thread(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
hello
主线程/主进程
''' #在主进程下开启子进程
t=Process(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
主线程/主进程
hello
'''
内存数据的共享问题
import os
import time
from threading import Thread
def func(a,b):
global g
g =
print(g,os.getpid()) g =
t_lst = []
for i in range():
t = Thread(target=func,args=(i,))
t.start()
t_lst.append(t)
for t in t_lst : t.join()
print(g)
输出结果
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0
进程是最小的内存分配单位
线程是操作系统调度的最小单位
线程被cpu执行了
进程内至少含有一个线程
进程中可以开启多个线程.
开启一个线程锁需要的时间远远小于开启一个进程。
多个线程内部有自己的数据栈,数据不共享。
全局变量在多个线程之间是共享的.
线程守护进程
1 .守护进程随着主进程代码执行的结束而结束.
2.守护线程会在主线程结束之后等待其他子线程结束而结束.
import time
from threading import Thread
def func1():
while True:
print('*'*)
time.sleep()
def func2():
print('in func2')
time.sleep() t = Thread(target=func1,)
t.daemon = True #守护线程在主线程结束之后会随着其他子线程的结束而结束。
t.start()
t2 = Thread(target=func2,)
t2.start()
t2.join()
print('主线程')
输出结果:
**********
in func2
主线程
**********
**********
**********
**********
**********
Join方法 :主线程一直等待全部的子线程结束之后,主线程自身才结束,程序退出。
join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止,
import time
from threading import Thread
def func1():
while True:
print('*'*)
time.sleep()
def func2():
print('in func2')
time.sleep() t = Thread(target=func1,)
t.daemon = True
t.start()
t2 = Thread(target=func2,)
t2.start()
t2.join()
print('主线程')
输出结果
D:\PycharmProjects\test\venv\Scripts\python.exe "D:/parcharm/12/Day 30/2.py"
**********
in func2
**********
**********
**********
**********
**********
主线程
线程Lock (互斥锁) 只有一把钥匙
线程的数据是共享的所以,一般我们会上锁,而 进程一般不会上锁锁,因为数据本身是隔离的。
# noodle_lock = Lock()
# fork_lock = Lock()
# def eat1(name):
# noodle_lock.acquire()
# print('%s拿到面条啦'%name)
# fork_lock.acquire()
# print('%s拿到叉子了'%name)
# print('%s吃面'%name)
# fork_lock.release()
# noodle_lock.release()
#
# def eat2(name):
# fork_lock.acquire()
# print('%s拿到叉子了'%name)
# time.sleep()
# noodle_lock.acquire()
# print('%s拿到面条啦'%name)
# print('%s吃面'%name)
# noodle_lock.release()
# fork_lock.release()
#
# Thread(target=eat1,args=('alex',)).start()
# Thread(target=eat2,args=('Egon',)).start()
# Thread(target=eat1,args=('bossjin',)).start()
# Thread(target=eat2,args=('nezha',)).start()
输出结果
D:\PycharmProjects\test\venv\Scripts\python.exe "D:/parcharm/12/Day 30/2.py"
alex拿到面条啦
alex拿到叉子了
alex吃面
Egon拿到叉子了
bossjin拿到面条啦
递归锁 RLOCK (递归锁.)解决死锁问题.
from threading import RLock # 递归锁
fork_lock = noodle_lock = RLock() # 一个钥匙串上的两把钥匙
def eat1(name):
noodle_lock.acquire() # 一把钥匙
print('%s拿到面条啦'%name)
fork_lock.acquire()
print('%s拿到叉子了'%name)
print('%s吃面'%name)
fork_lock.release()
noodle_lock.release() def eat2(name):
fork_lock.acquire()
print('%s拿到叉子了'%name)
time.sleep(1)
noodle_lock.acquire()
print('%s拿到面条啦'%name)
print('%s吃面'%name)
noodle_lock.release()
fork_lock.release() Thread(target=eat1,args=('alex',)).start()
Thread(target=eat2,args=('Egon',)).start()
Thread(target=eat1,args=('bossjin',)).start()
Thread(target=eat2,args=('nezha',)).start()
信号量和事件Event
信号量 :同一时间允许多个线程访问一段数据。
import time
from threading import Semaphore,Thread
def func(sem,a,b):
sem.acquire()
time.sleep()
print(a+b)
sem.release() sem = Semaphore()
for i in range():
t = Thread(target=func,args=(sem,i,i+))
t.start()
输出结果:
23
同时输出4个数据。
事件
# 事件被创建的时候
# False状态
# wait() 阻塞
# True状态
# wait() 非阻塞
# clear 设置状态为False
# set 设置状态为True
# 数据库 - 文件夹
# 文件夹里有好多excel表格
# 1.能够更方便的对数据进行增删改查
# 2.安全访问的机制
# 起两个线程
# 第一个线程 : 连接数据库
# 等待一个信号 告诉我我们之间的网络是通的
# 连接数据库
# 第二个线程 : 检测与数据库之间的网络是否连通
# time.sleep(0,2) 2
# 将事件的状态设置为True
import time
import random
from threading import Thread,Event
def connect_db(e):
count = 0
while count < 3:
e.wait(0.5) # 状态为False的时候,我只等待1s就结束
if e.is_set() == True:
print('连接数据库')
break
else:
count += 1
print('第%s次连接失败'%count)
else:
raise TimeoutError('数据库连接超时') def check_web(e):
time.sleep(random.randint(0,3))
e.set() e = Event()
t1 = Thread(target=connect_db,args=(e,))
t2 = Thread(target=check_web,args=(e,))
t1.start()
t2.start()
条件condition(更为复杂的锁.)
acquire release notify wait 四种方法.
一个条件被创建初,默认有一个false
notify 和wait 必须在release 和acquire 之间。
# 条件
from threading import Condition # 条件
# 锁
# acquire release
# 一个条件被创建之初 默认有一个False状态
# False状态 会影响wait一直处于等待状态
# notify(int数据类型) 造钥匙
from threading import Thread,Condition
def func(con,i):
con.acquire()
con.wait() # 等钥匙
print('在第%s个循环里'%i)
con.release()
con = Condition()
for i in range(10):
Thread(target=func,args = (con,i)).start()
while True:
num = int(input('>>>'))
con.acquire()
con.notify(num) # 造钥匙
con.release()
定时器 Timer)
import time
from threading import Timer
def func():
print('时间同步') #1-3 while True:
t = Timer(5,func).start() # 非阻塞的
time.sleep(5)
等待五秒执行func里的代码
Python标准模块--concurrent.futures
import time
from concurrent.futures import ThreadPoolExecutor
def func(n):
time.sleep(2)
print(n)
return n*n def call_back(m):
print('结果是 %s'%m.result()) tpool = ThreadPoolExecutor(max_workers=5) # 默认 不要超过cpu个数*5
for i in range(20):
tpool.submit(func,i).add_done_callback(call_back) # tpool.map(func,range(20)) # 拿不到返回值
# t_lst = []
# for i in range(20):
# t = tpool.submit(func,i)
# t_lst.append(t)
# tpool.shutdown() # close+join #
# print('主线程')
# for t in t_lst:print('***',t.result()) # ftp
# 并发编程
队列
Queue
q =queue.Queue() 队列先进先出
import queue q=queue.Queue()
q.put('first')
q.put('second')
q.put('third') print(q.get())
print(q.get())
print(q.get())
输出结果.
first
second
third
q = queue.LifoQueue() 栈 ,先进后出
import queue q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third') print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
third
second
first
'''
优先级多列
q= queue.PriorityQueue()
import queue
q =queue.PriorityQueue()
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))
q.put((-5,'d'))
q.put((1,'?'))
print(q.get())
结果:
D:\PycharmProjects\test\venv\Scripts\python.exe "D:/parcharm/12/Day 30/2.py"
(-5, 'd')
Day 41 线程的更多相关文章
- 4-1 线程安全性-原子性-atomic-1
我们发现在不做任何同步的情况下,我们计算的累加结果是错误的. com.mmall.concurrency.example.count.CountExample2 C:\Users\ZHONGZHENH ...
- Java线程:概念与原理
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Java线程:线程状态的转换
Java线程:线程状态的转换 一.线程状态 线程的状态转换是线程控制的基础.线程状态总的可分为五大状态:分别是生.死.可运行.运行.等待/阻塞.用一个图来描述如下: 1.新状态:线程对象已 ...
- java线程详解
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Java线程详解----借鉴
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- java 线程(1)
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Java 线程的创建和启动
Java 使用 Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例.每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码). Java 使用线程执 ...
- java线程大全一讲通
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Python_oldboy_自动化运维之路_线程,进程,协程(十一)
本节内容: 线程 进程 协程 IO多路复用 自定义异步非阻塞的框架 线程和进程的介绍: 举个例子,拿甄嬛传举列线程和进程的关系: 总结:1.工作最小单元是线程,进程说白了就是提供资源的 2.一个应用程 ...
随机推荐
- php中错误处理机制
php中,异常处理机制是有限的,无法自动抛出异常,必须手动进行,并且内置异常有限. php把许多异常看作错误,这样就可以把这些异常想错误一样用set_error_handler接管,进而主动抛出异常. ...
- Scrum使用心得 【转】
原文链接: http://blog.sina.com.cn/s/blog_58db96bc0100ymuk.html 1 Scrum管理模式和传统管理模式的区别 这些管理模式本质上目的相同: ...
- 创建和修改主键 (SQL)
添加主键, ALTER TABLE [表名:OrderInfo] Add PRIMARY KEY ([列名:ProductID, UserID...]) 多个列则是组合主键 删除主键, ALTER ...
- css浮动的元素居中
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- JavaScript开发者的工具箱
自从HTML5变得流行以来,整个Web平台取得了长足的进步,人们也开始将JavaScript视为一门能够创建复杂应用的语言.许多新的API纷纷浮现,而关于浏览器如何应用这些技术的文章也大量涌现. 作为 ...
- 关于前一篇innodb自增列自己的一点补充
上篇文章是我转载的,忘记注明了出处,在这里深感歉意.但是上篇文章中关于自增列预留ID的计算我当时怎么弄明白,后来自己想了想终于想通了,在这里详细解释一下. 我们以一次性插入10行为例,表格如下: 插 ...
- HTTP 500.21 处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler”
问题:处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler” IIS7下部署网站出现错误,原因是先安装的II ...
- 如何查看路由器的mac和计算机的mac
如何查看路由器的mac和计算机的mac 一.查看路由器的mac 方法一: 直接看路由器的背面,如下图,即可看到MAC地址 打开命令提示符窗口,输入ipconfig,找到网关地址,如192.168. ...
- java--多线程编程简介
1.什么时候使用多线程编程 一个任务在正常情况下是按顺序执行的,但是如果当前任务里有多个相似进程块(例如for,while语句),我们就可以考虑把这些代码块抽出来并行运行,无需阻塞 2.实现多线程的几 ...
- "我们分手吧。"女的对男的说。 "为什么呢?亲爱的,你不要我了么?" "因为你幼稚。"女的坚定地语气回答道,然后转身准备走。 男的上前踩住女的影子,然后说...
1."我们分手吧."女的对男的说. "为什么呢?亲爱的,你不要我了么?" "因为你幼稚."女的坚定地语气回答道,然后转身准备走. 男的上前踩 ...