diff --git a/03.网络编程与并发/04.多线程.md b/03.网络编程与并发/04.多线程.md index e2e6d3a..6ab1874 100644 --- a/03.网络编程与并发/04.多线程.md +++ b/03.网络编程与并发/04.多线程.md @@ -31,7 +31,7 @@ ### 使用线程的实际场景 -开启一个字处理软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作的都是同一块数据,因而不能用多进程。只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。 +开启一个软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作的都是同一块数据,因而不能用多进程。只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。 ### 内存中的线程 @@ -50,9 +50,9 @@ ### 全局解释器锁GIL Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。 -  对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。 +对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。 -  在多线程环境中,Python 虚拟机按以下方式执行: +在多线程环境中,Python 虚拟机按以下方式执行: 1. 设置 GIL; @@ -65,7 +65,8 @@ Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Pyt 5. 解锁 GIL; 6. 再次重复以上所有步骤。 - 在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。 + +在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序可以主动解锁GIL。 ### python线程模块的选择 @@ -87,7 +88,7 @@ def sayhi(name): print('%s say hello' %name) if __name__ == '__main__': - t=Thread(target=sayhi,args=('aaron',)) + t=Thread(target=sayhi,args=('张三',)) t.start() print('主线程') ``` @@ -107,7 +108,7 @@ class Sayhi(Thread): if __name__ == '__main__': - t = Sayhi('aaron') + t = Sayhi('张三') t.start() print('主线程') ``` @@ -256,7 +257,7 @@ import os def work(): import time time.sleep(3) - print(threading.current_thread().getName()) + print(threading.current_thread().name) if __name__ == '__main__': @@ -264,7 +265,7 @@ if __name__ == '__main__': t=Thread(target=work) t.start() - print(threading.current_thread().getName()) + print(threading.current_thread().name) print(threading.current_thread()) #主线程 print(threading.enumerate()) #连同主线程在内有两个运行的线程 print(threading.active_count()) @@ -281,7 +282,7 @@ def sayhi(name): print('%s say hello' %name) if __name__ == '__main__': - t=Thread(target=sayhi,args=('aaron',)) + t=Thread(target=sayhi,args=('张三',)) t.start() t.join() print('主线程') @@ -305,8 +306,8 @@ def sayhi(name): print('%s say hello' %name) if __name__ == '__main__': - t=Thread(target=sayhi,args=('aaron',)) - t.setDaemon(True) #必须在t.start()之前设置 + t=Thread(target=sayhi,args=('张三',)) + t.daemon = True #必须在t.start()之前设置 t.start() print('主线程') @@ -433,8 +434,7 @@ from threading import current_thread,Thread,Lock import os,time def task(): #未加锁的代码并发运行 - time.sleep(3) - print('%s start to run' %current_thread().getName()) + print('%s start to run' %current_thread().name) global n #加锁的代码串行运行 lock.acquire() @@ -459,34 +459,10 @@ if __name__ == '__main__': ``` 有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊 -没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是 +没错:在start之后立刻使用join,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是 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)) -``` - ### 死锁与递归锁 两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为**死锁进程** diff --git a/03.网络编程与并发/05.多协程.md b/03.网络编程与并发/05.多协程.md index b2b7a1a..fe7d0af 100644 --- a/03.网络编程与并发/05.多协程.md +++ b/03.网络编程与并发/05.多协程.md @@ -127,7 +127,7 @@ from greenlet import greenlet def eat(name): print('%s eat 1' %name) - g2.switch('aaron') + g2.switch('张三') print('%s eat 2' %name) g2.switch() def play(name): @@ -138,7 +138,7 @@ def play(name): g1=greenlet(eat) g2=greenlet(play) -g1.switch('aaron') # 可以在第一次switch时传入参数,以后都不需要 +g1.switch('张三') # 可以在第一次switch时传入参数,以后都不需要 ``` 单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度 @@ -224,8 +224,8 @@ def play(name): print('%s play 2' %name) -g1=gevent.spawn(eat,'aaron') -g2=gevent.spawn(play,name='aaron') +g1=gevent.spawn(eat,'张三') +g2=gevent.spawn(play,name='张三') g1.join() g2.join() #或者gevent.joinall([g1,g2]) @@ -259,7 +259,7 @@ gevent.joinall([g1,g2]) print('主') ``` -用threading.current_thread().getName()来查看每个g1和g2,查看的结果为DummyThread-n,即假线程 +用threading.current_thread().name来查看每个g1和g2,查看的结果为DummyThread-n,即假线程 ```python from gevent import monkey;monkey.patch_all() @@ -267,13 +267,13 @@ import threading import gevent import time def eat(): - print(threading.current_thread().getName()) + print(threading.current_thread().name) print('eat food 1') time.sleep(2) print('eat food 2') def play(): - print(threading.current_thread().getName()) + print(threading.current_thread().name) print('play 1') time.sleep(1) print('play 2') @@ -372,6 +372,8 @@ def talk(conn,addr): try: while True: res=conn.recv(1024) + if len(res) == 0: + continue print('client %s:%s msg: %s' %(addr[0],addr[1],res)) conn.send(res.upper()) except Exception as e: @@ -389,7 +391,7 @@ if __name__ == '__main__': from socket import * client=socket(AF_INET,SOCK_STREAM) -client.connect(('127.0.0.1',8080)) +client.connect(('127.0.0.1',8088)) while True: @@ -414,7 +416,7 @@ def client(server_ip,port): count=0 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) print(msg.decode('utf-8')) count+=1