09-19-周五_14-48-32

This commit is contained in:
2025-09-19 14:48:32 +08:00
parent 66148187d0
commit ef0ed27b9e
2 changed files with 25 additions and 47 deletions

View File

@@ -31,7 +31,7 @@
### 使用线程的实际场景 ### 使用线程的实际场景
开启一个字处理软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作的都是同一块数据,因而不能用多进程。只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。 开启一个软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作的都是同一块数据,因而不能用多进程。只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。
### 内存中的线程 ### 内存中的线程
@@ -50,9 +50,9 @@
### 全局解释器锁GIL ### 全局解释器锁GIL
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。 Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
  对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。 对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
  在多线程环境中Python 虚拟机按以下方式执行: 在多线程环境中Python 虚拟机按以下方式执行:
1. 设置 GIL 1. 设置 GIL
@@ -65,7 +65,8 @@ Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Pyt
5. 解锁 GIL 5. 解锁 GIL
6. 再次重复以上所有步骤。 6. 再次重复以上所有步骤。
在调用外部代码(如 C/C++扩展函数)的时候GIL将会被锁定直到这个函数结束为止(由于在这期间没有Python的字节码被运行所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
在调用外部代码(如 C/C++扩展函数)的时候GIL将会被锁定直到这个函数结束为止(由于在这期间没有Python的字节码被运行所以不会做线程切换)编写扩展的程序可以主动解锁GIL。
### python线程模块的选择 ### python线程模块的选择
@@ -87,7 +88,7 @@ def sayhi(name):
print('%s say hello' %name) print('%s say hello' %name)
if __name__ == '__main__': if __name__ == '__main__':
t=Thread(target=sayhi,args=('aaron',)) t=Thread(target=sayhi,args=('张三',))
t.start() t.start()
print('主线程') print('主线程')
``` ```
@@ -107,7 +108,7 @@ class Sayhi(Thread):
if __name__ == '__main__': if __name__ == '__main__':
t = Sayhi('aaron') t = Sayhi('张三')
t.start() t.start()
print('主线程') print('主线程')
``` ```
@@ -256,7 +257,7 @@ import os
def work(): def work():
import time import time
time.sleep(3) time.sleep(3)
print(threading.current_thread().getName()) print(threading.current_thread().name)
if __name__ == '__main__': if __name__ == '__main__':
@@ -264,7 +265,7 @@ if __name__ == '__main__':
t=Thread(target=work) t=Thread(target=work)
t.start() t.start()
print(threading.current_thread().getName()) print(threading.current_thread().name)
print(threading.current_thread()) #主线程 print(threading.current_thread()) #主线程
print(threading.enumerate()) #连同主线程在内有两个运行的线程 print(threading.enumerate()) #连同主线程在内有两个运行的线程
print(threading.active_count()) print(threading.active_count())
@@ -281,7 +282,7 @@ def sayhi(name):
print('%s say hello' %name) print('%s say hello' %name)
if __name__ == '__main__': if __name__ == '__main__':
t=Thread(target=sayhi,args=('aaron',)) t=Thread(target=sayhi,args=('张三',))
t.start() t.start()
t.join() t.join()
print('主线程') print('主线程')
@@ -305,8 +306,8 @@ def sayhi(name):
print('%s say hello' %name) print('%s say hello' %name)
if __name__ == '__main__': if __name__ == '__main__':
t=Thread(target=sayhi,args=('aaron',)) t=Thread(target=sayhi,args=('张三',))
t.setDaemon(True) #必须在t.start()之前设置 t.daemon = True #必须在t.start()之前设置
t.start() t.start()
print('主线程') print('主线程')
@@ -433,8 +434,7 @@ from threading import current_thread,Thread,Lock
import os,time import os,time
def task(): def task():
#未加锁的代码并发运行 #未加锁的代码并发运行
time.sleep(3) print('%s start to run' %current_thread().name)
print('%s start to run' %current_thread().getName())
global n global n
#加锁的代码串行运行 #加锁的代码串行运行
lock.acquire() lock.acquire()
@@ -459,34 +459,10 @@ if __name__ == '__main__':
``` ```
有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊 有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是 没错:在start之后立刻使用join,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的 start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高. 单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
```python
from threading import current_thread,Thread,Lock
import os,time
def task():
time.sleep(3)
print('%s start to run' %current_thread().getName())
global n
temp=n
time.sleep(0.5)
n=temp-1
if __name__ == '__main__':
n=100
lock=Lock()
start_time=time.time()
for i in range(100):
t=Thread(target=task)
t.start()
t.join()
stop_time=time.time()
print('主:%s n:%s' %(stop_time-start_time,n))
```
### 死锁与递归锁 ### 死锁与递归锁
两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为**死锁进程** 两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为**死锁进程**

View File

@@ -127,7 +127,7 @@ from greenlet import greenlet
def eat(name): def eat(name):
print('%s eat 1' %name) print('%s eat 1' %name)
g2.switch('aaron') g2.switch('张三')
print('%s eat 2' %name) print('%s eat 2' %name)
g2.switch() g2.switch()
def play(name): def play(name):
@@ -138,7 +138,7 @@ def play(name):
g1=greenlet(eat) g1=greenlet(eat)
g2=greenlet(play) g2=greenlet(play)
g1.switch('aaron') # 可以在第一次switch时传入参数以后都不需要 g1.switch('张三') # 可以在第一次switch时传入参数以后都不需要
``` ```
单纯的切换在没有io的情况下或者没有重复开辟内存空间的操作反而会降低程序的执行速度 单纯的切换在没有io的情况下或者没有重复开辟内存空间的操作反而会降低程序的执行速度
@@ -224,8 +224,8 @@ def play(name):
print('%s play 2' %name) print('%s play 2' %name)
g1=gevent.spawn(eat,'aaron') g1=gevent.spawn(eat,'张三')
g2=gevent.spawn(play,name='aaron') g2=gevent.spawn(play,name='张三')
g1.join() g1.join()
g2.join() g2.join()
#或者gevent.joinall([g1,g2]) #或者gevent.joinall([g1,g2])
@@ -259,7 +259,7 @@ gevent.joinall([g1,g2])
print('主') print('主')
``` ```
用threading.current_thread().getName()来查看每个g1和g2查看的结果为DummyThread-n即假线程 用threading.current_thread().name来查看每个g1和g2查看的结果为DummyThread-n即假线程
```python ```python
from gevent import monkey;monkey.patch_all() from gevent import monkey;monkey.patch_all()
@@ -267,13 +267,13 @@ import threading
import gevent import gevent
import time import time
def eat(): def eat():
print(threading.current_thread().getName()) print(threading.current_thread().name)
print('eat food 1') print('eat food 1')
time.sleep(2) time.sleep(2)
print('eat food 2') print('eat food 2')
def play(): def play():
print(threading.current_thread().getName()) print(threading.current_thread().name)
print('play 1') print('play 1')
time.sleep(1) time.sleep(1)
print('play 2') print('play 2')
@@ -372,6 +372,8 @@ def talk(conn,addr):
try: try:
while True: while True:
res=conn.recv(1024) res=conn.recv(1024)
if len(res) == 0:
continue
print('client %s:%s msg: %s' %(addr[0],addr[1],res)) print('client %s:%s msg: %s' %(addr[0],addr[1],res))
conn.send(res.upper()) conn.send(res.upper())
except Exception as e: except Exception as e:
@@ -389,7 +391,7 @@ if __name__ == '__main__':
from socket import * from socket import *
client=socket(AF_INET,SOCK_STREAM) client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080)) client.connect(('127.0.0.1',8088))
while True: while True:
@@ -414,7 +416,7 @@ def client(server_ip,port):
count=0 count=0
while True: while True:
c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8')) c.send(('%s say hello %s' %(threading.current_thread().name,count)).encode('utf-8'))
msg=c.recv(1024) msg=c.recv(1024)
print(msg.decode('utf-8')) print(msg.decode('utf-8'))
count+=1 count+=1