1.4.2-2.1.11
1、tcp注意点
1)tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器
2)tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机
3)tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的
4)当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信
5)当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务
6)listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的,关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。关闭accept返回的套接字意味着这个客户端已经服务完毕
7)当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线
2、并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
3、并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的
import threading # 线程需要引入threading模块
from time import sleep,time
def sing():
for i in range(3):
print(“正在唱歌…%d”%i)
sleep(1)
def dance():
for i in range(3):
print(“正在跳舞…%d”%i)
sleep(1)
if name == ‘main’:
print(’—开始—:%s’%ctime())
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance) # 创建了一个线程对象
t1.start() #创建并运行线程
t2.start()
while True:
length = len(threading.enumerate())
print('当前运行的线程数为:%d'%length) #查看线程的数量
if length<=1:
break
sleep(0.5)
主线程会在子线程结束后才会结束
4、在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
5、缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)
6、如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确
7、为解决资源竞争,需要加锁
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
互斥锁为资源引入一个状态:锁定/非锁定
threading模块中定义了Lock类,可以方便的处理锁定:
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire()
#释放
mutex.release()
注意:
1)如果这个锁之前是没有上锁的,那么acquire不会堵塞
2)如果在调用acquire对这个锁上锁之前 它已经被 其他线程上了锁,那么此时acquire会堵塞,直到这个锁被解锁为止
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
8、锁的好处:
确保了某段关键代码只能由一个线程从头到尾完整地执行
9、锁的坏处:
阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁