线程
*线程也是多任务编程的一种方法,可以使用计算机多核资源
*线程又称为轻量级的进程,在创建和删除时消耗的计算机资源小
线程和进程关系:
*一个进程中可以包含多个线程
*进程中的所有线程共享进程的空间资源(空间,全局变量,分配的内存等)
*线程也有自己的特有属性,比如:指令集 TID等
创建线程
import threading
t=threading.Tread()
Thread()
功能:创建线程
参数:target 线程函数
args 元组 给线程函数位置传参
kwargs 字典 给线程函数字典传参
name 给线程取名字 (默认Thread-1)
返回值:线程对象
t.start() 启动线程
t.join(timeout) 回收线程
t.is_alive() 查看线程状态
t.name 查看线程名称
threading.currentThread() 得到线程对象
t.setName() 设置属性名称
t.daemon 属性
默认为False 主线程执行完毕不会影响分支线程的执行
设为True 主线程执行完毕,其他线程也会终止
t.isDaemon():判断 deamon属性时True or False
设置方法:
t.daemon=True
t.setDaemon(True)
示例一:
import threading
from time import sleep
def music():
while True:
sleep(2)
print("播放《活着》")
def sing():
while True:
sleep(2)
print("kjkjk")
#创建线程和函数music关联
t=threading.Thread(target=music)
a=threading.Thread(target=sing)
#启动线程
t.start()
a.start()
while True:
sleep(1.5)
print("想听《花海》")
print("=======")
t.join()
a.join()
示例二:
#线程属性
from threading import Thread,currentThread
from time import sleep
def fun(sec):
print("线程属性测试")
sleep(sec)
print("%s线程结束"%currentThread().getName())
thread=[]
for i in range(3):
t=Thread(name='tedu'+str(i),target=fun,args=(5,))
t.start()
thread.append(t)
for i in thread:
print("thread name :",i.name)
print("alive :",i.is_alive())
i.join()
示例三:
#daemon属性
from threading import Thread
from time import sleep
def fun():
print("Daemon 属性测试")
sleep(5)
print("线程结束")
t=Thread(target=fun)
#为线程设置名字
t.setName('tedu')
#daemon属性设置为True
#t.daemon=True
t.setDaemon(True)#将daemon属性设置为True
print(t.isDaemon())
t.start()
t.join(2)
print("=====主线程结束=======")
线程间通信
通过全局变量进行通信
示例:
from threading import Thread
from time import sleep
#全局变量a
#通过全局变量进行通信
a=1
def foo():
global a
#将全局变量a做修改
a=1000
def bar():
sleep(1)
#此时全局变量a改变,变为100
print("a=",a)#a=1000
t1=Thread(target=foo)
t2=Thread(target=bar)
t1.start()
t2.start()
t1.join()
t2.join()
print(a)
进程和线程的区别和联系
两者都是多任务编程方式,均可使用计算机的多核
进程的创建要比线程消耗更多的资源
进程空间独立,数据安全性更好操作,有专门的进程间通信方式
线程使用全局变量通信,往往要和同步互斥机制配合防止产生资源的争夺
一个进程可以包含多个线程,线程共享进程资源
进程线程都有自己的特有资源
使用场景
需要创建较多的并发,任务比较简单,线程比较合适
如果程序间数据资源使用重叠比较多,要考虑到线程锁是否需要更复杂的逻辑
如果多个任务并无什么关联,不易用多线程将其融入到一个进程中
python线程不适用计算密集型并发
创建自定义线程类
继承Thread类
重写 run 方法
from threading import Thread
from time import ctime,sleep
#编写自己的线程类
class Mythread(Thread):
def __init__(self,func,args,name='Tedu'):
# Thread.__init__(self)
super().__init__()
self.func=func
self.args=args
self.name=name
#调用start会自动运行
def run(self):
self.func(*self.args)
def player(file,sec):
for i in range(2):
print("playing %s :%s"%(file,ctime()))
sleep(sec)
t=Mythread(player,('baby.mp3',3))
t.start()
t.join()
示例:
from threading import Thread
from time import ctime,sleep
#编写自己的线程类
class Mythread(Thread):
def __init__(self,func,args,name='Tedu'):
#如果没有执行父类__init__方法
# Thread.__init__(self)
super().__init__()
self.func=func
self.args=args
self.name=name
#调用start会自动运行
#重写run方法
def run(self):
player() args=('baby.mp3',3)
self.func(*args)
def player(file,sec):
for i in range(2):
print("playing %s :%s"%(file,ctime()))
sleep(sec)
t=Mythread(player,('baby.mp3',3))
t.start()
t.join()
线程中同步互斥方法
Event 线程事件
e=Event()
e.wait() e.set() e.clear()
示例:
import threading
from time import sleep
s=None
#创建线程对象
e=threading.Event()
def fun1():
print("和fun2进行沟通")
global s
s="天街小雨润如酥"
def fun2():
print("等待口令")
sleep(2)
if s == "天街小雨润如酥":
print("fun2收到",s)
else:
print("口令有误")
e.set()
def fun3():
print("改掉口令")
sleep(1)
e.wait()#阻塞
global s
s="不下了"#想要将全局变量s改为"不下了",导致fun2接受的消息将发生改变
t1=threading.Thread(name='fun1',taregt=fun1)
t2=threading.Thread(name='fun2',taregt=fun2)
t3=threading.Thread(name='fun3',taregt=fun3)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
Lock 线程锁
t=Loce() 创建锁
t.acquire() 加锁
t.release() 解锁
示例:
import threading
a=b=0
lock=threading.Lock()
def value():
while True:
lock.acquire()#加锁
if a!=b:
print("a=%d,b=%d"%(a,b))
lock.release()#解锁
t=threading.Thread(target=value)
t.start()
while True:
lock.acquire()
a+=1
b+=1
lock.release()
t.join()
Condition
示例:
import threading
import time
import datetime
num=0
#条件变量
con=threading.Condition()
class Gov(threading.Thread):
def run(self):
global num
con.acquire()
while True:
print("开始拉升股市")
num+=1
print("拉升了%d个点"%num)
time.sleep(2)
if num==5:
print("暂时安全")
con.notify()
print("不操作")
con.wait()
con.release()
class Consumers(threading.Thread):
def run(self):
global num
con.acquire()
while True:
if num>0:
print("开始打压股市")
num-=1
print("下降了%d个点"%num)
time.sleep(2)
if num==0:
print("天台在哪,我要自杀")
con.notify()
print("不能再下降了")
con.wait()
con.release()
t1=Gov()
t2=Consumers()
t1.start()
t2.start()
python线程之GIL
(全局解释器锁)
python ---->支持多线程---->同步互斥---->加锁--->超级锁--->在同一时刻,解释器只能解释一个线程---->大量python库为了省事沿用了这种方法--->python 多线程效率低下
GIL问题:由于python的全局解释器锁造成python的多线程执行效率低下
解决方法:
*不使用线程,使用多进程
*不适用C C++ 做解释器 C# Java也可以做解释器
*python线程适合高用时的IO操作,网络IO,不适合cpu密集型程序
多线程并发
相比多进程并发:
劣势:
1.可能需要同步和互斥的机制
2.受到GIL的影响
优势: 只需要消耗较少的系统资源
步骤:
1.创建套接字
2.准备接收客户端连接
3.每当有一个客户端链接进来就创建一个新的线程
4.客户端退出后结束相应线程,关闭客户端套接字
from socket import *
from threading import *
import os,sys
#处理具体的客户端请求
def handler(connfd):
print("Got connection from ",connfd.getpeername())
while True:
data=connfd.recv(1024).decode()
if not data:
break
connfd.send(b"receive your message")
connfd.close()
HOST='172.60.16.43'
PORT=8814
#创建套接字
s=socket()
s.bind((HOST,PORT))
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.listen(5)
#主线程循环接受客户端连接
while True:
try:
c,addr=s.accept()
except KeyboardInterrupt:
raise
except Exception as e:
print(e)
continue
t=Thread(target=handler,args=(c,))
t.setDaemon(True)
t.start()
s.close()