目录
一、线程
1.1 进程和线程的关系
1.2 线程的特点
1.3 内存中的线程
1.4 线程启动格式代码:
1.5 练习 :多线程实现socket
二、全局解释器锁GIL
三、Thread类的其他方法
进程是资源分配的最小单位,线程是CPU调度的最小单位.每一个进程中至少有一个线程。
线程与进程的区别可以归纳为以下4点:
1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
3)调度和切换:线程上下文切换比进程上下文切换要快得多。
4)在多线程操作系统中,进程不是一个可执行的实体。
通过漫画了解进程
1)轻型实体
线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。
线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述。
2)独立调度和分派的基本单位。
在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。
3)共享进程资源。
线程在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
4)可并发执行。
在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。
方法一:和多进程差不多
import os
from threading import Thread
多线程并发
def func(a,b):
global g
g = 0
print(g,os.getpid())
g = 100
t_lst = []
for i in range(10):
t = Thread(target=func,args=(i,5))
t.start()
t_lst.append(t)
for t in t_lst : t.join()
print(g)
0 5408
0 5408
0 5408
0 5408
0 5408
0 5408
0 5408
0 5408
0 5408
0 5408
0
可以使用global修改主进程中的变量。
方法二:面向对象
import os
import time
from threading import Thread
class MyTread(Thread):
def __init__(self,args):
super().__init__()
self.args = args
def run(self):
time.sleep(1)
print(self.args)
t = MyTread(10)
t.start()
总结:
# 进程是最小的内存分配单位
# 线程是操作系统调度的最小单位
# 线程直接被CPU执行,进程内至少含有一个线程,也可以开启多个线程
# 开启一个线程所需要的时间要远远小于开启一个进程
# 多个线程内部有自己的数据栈,数据不共享
# 全局变量在多个线程之间是共享的
client端:
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
msg = sk.recv(1024)
print(msg)
inp = input('>>> ').encode('utf-8')
sk.send(inp)
sk.close()
serve端:
import socket
from threading import Thread
def chat(conn):
conn.send(b'hello')
msg = conn.recv(1024).decode('utf-8')
print(msg)
conn.close()
sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
while True:
conn,addr = sk.accept()
Thread(target=chat,args = (conn,)).start()
sk.close()
图解:(锁能保证同一时刻只有一个线程读取数据)
在多线程环境中,按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。
在Cpython解释器下的python程序 在同一时刻 多个线程中只能有一个线程被CPU执行
高CPU : 计算类 --- 高CPU利用率 多进程(计算复杂)
高IO : 爬取网页 200个网页 多线程(高并发)
qq聊天 send recv
处理日志文件 读文件
处理web请求
读数据库 写数据库
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,
Thread实例对象的方法
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。
threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
import time
import threading
def wahaha(n):
time.sleep(0.5)
print(n,threading.current_thread(),threading.get_ident())
for i in range(10):
threading.Thread(target=wahaha,args=(i,)).start()
print(threading.active_count()) # 一共有几个活跃的线程
print(threading.current_thread()) # 返回线程名称和id
print(threading.enumerate()) # 列表的形式返回所有线程名称和id