1、概念
- 线程: 线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
- 多线程: (英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多个线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
- 多线程优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
- 程序的运行速度可能加快。
- 在一些等待的任务实现上如用户输入、文件读取和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源,
如内存暂用等等。
具体可参见:百度百科-线程
2、线程创建
线程状态 :新建 就绪 运行 阻塞 结束
import threading
from time import sleep
#进程: Process
#线程: Thread
def download(n):
images = ['girl.jpg','boy.jpg','man.jpg']
for image in images:
print('正在下载:',image)
sleep(n) #给出cpu运行权,阻塞
print('下载{}成功'.format(image))
def listenMusic():
musics =['一荤一素','奉献','隔壁小王','耳朵']
for music in musics:
sleep(0.8) #给出cpu运行权,阻塞
print('正在听{}歌!'.format(music))
if __name__ == "__main__":
#线程对象
t1 = threading.Thread(target = download,name='aa',args=(1,))
t1.start()
t2 = threading.Thread(target = listenMusic,name='bb')
t2.start()
n = 1
while True:
print(n)
sleep(1) #给出cpu运行权,阻塞
n = n+1
3、线程共享数据
import threading
money = 1000
def run1():
global money
for i in range(100):
money -= 1
def run2():
global money
for i in range(100):
money -= 1
if __name__ == '__main__':
#创建线程
th1 = threading.Thread(target=run1,name='th1')
th2 = threading.Thread(target=run2,name='th2')
th1.start()
th2.start()
th1.join()
th2.join()
print('money',money)
4、线程同步
引入:
线程是可以共享全局变量的,线程存在数据安全的问题!
保证数据安全,就需要进程同步,但这样也有缺点,速度慢!Python底层只要用线程就会默认加锁,使得进程同步!
进程同步默认:GIL 全局解释器锁,但是但是,达到一定的运算量这个锁会自动解开!
线程运用:耗时操作,爬虫
同步:一个一个的完成,一个做完另一个才能进去!但是效率会降低!保证数据的准确性!使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于哪些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间!
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可以存在数据不同步的问题。为了避免这种情况,引入了锁的概念!
lock = threading.Lock()
lock.acquire() 请求得到锁
......
lock.release() 解开锁
源代码:
import threading
import random
import time
lock = threading.Lock()
list1 = [0]*10 #表示列表中有10个0
def task1():
#获取线程锁,如果已经上锁,则等待锁的释放
lock.acquire() #阻塞
for i in range(len(list1)):
list1[i] = 1
time.sleep(0.5)
lock.release()
def task2():
lock.acquire() # 阻塞
for i in range(len(list1)):
print('---->',list1[i])
time.sleep(0.5)
lock.release() #释放锁
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
t1.join()
t2.join()
5、死锁
开发过程中使用线程,在线程共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。尽管死锁很少发生,但是一旦发生就会造成应用的停止响应,程序不做任何事情。
避免死锁:
1、重构代码
2、acquire(timeout=’’)加个timeout
#死锁的状态
from threading import Thread,Lock
import time
lockA = Lock()
lockB = Lock()
class MyThread1(Thread):
def run(self): #start()
if lockA.acquire(): #如果可以获取到锁则返回True
print(self.name+'获取了A锁')
time.sleep(1)
if lockB.acquire():
print(self.name+'又获取了A锁,原来还有B锁')
lockB.release()
lockA.release()
class MyThread2(Thread):
def run(self): #start()
if lockB.acquire(): #如果可以获取到锁则返回True
print(self.name+'获取了B锁')
time.sleep(1)
if lockA.acquire():
print(self.name+'又获取了B锁,原来还有A锁')
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()
t1.join()
t2.join()
6、生产者和消费者
生产者与消费者:两个线程之间的通信。
import threading
import queue
import random
import time
def produce(q):
i = 0
while i<10:
num = random.randint(1,100)
q.put("生产者产生数据:%d"% num)
print("生产者产生数据:%d"% num)
time.sleep(1)
i += 1
q.put(None)
#完成任务
q.task_done()
def consume(q):
while True:
item = q.get()
if item is None:
break
print('消费者获取到:%s'%item)
time.sleep(4)
#完成任务
q.task_done()
if __name__ == '__main__':
q = queue.Queue(10)
arr = []
#创建生产者
th = threading.Thread(target=produce,args=(q,))
th.start() #就绪状态
#创建消费者
tc = threading.Thread(target=consume,args=(q,))
tc.start()
th.join()
tc.join()
print('END')