并发就是多线程轮流使用同一进程的资源
进程里面可以存着多个线程,多个线程共用进程资源,因此存在多个线程争夺进程资源的问题

1. 线程状态:
新建,就绪,运行,阻塞,结束
阻塞完之后,会回到就绪上来


import threading
from time import sleep
def download(n):
images = ['1.jpg', '2.jpg', '3.jpg']
for image in images:
print('正在下载:', image)
sleep(n)
print('下载{}成功!:'.format(image))
def listen_music(n):
musics = ['大碗宽面', '土耳其冰淇淋', '烤面筋', '人民广场炸鸡']
for music in musics:
sleep(n)
print('正在听{}歌!'.format(music))
if __name__ == '__main__':
# 创建线程对象
t = threading.Thread(target=download, name='aaa', args=(1, ))
t.start()
t1 = threading.Thread(target=listen_music, name='bbb', args=(0.5, ))
t1.start()

线程是可以共享全局变量
import threading
money = 1000
def run1():
global money
for i in range(100):
money -= 1
if __name__ == '__main__':
th1 = threading.Thread(target=run1)
th2 = threading.Thread(target=run1)
th3 = threading.Thread(target=run1)
th4 = threading.Thread(target=run1)
th1.start()
th2.start()
th3.start()
th4.start()
th1.join()
th2.join()
th3.join()
th4.join()
print('money:', money)
输出:


2.线程同步
GIL(Global Interpreter Lock)全局解释器锁
先明确一个问题,Python中的多线程是假的多线程! 为什么这么说,我们先明确一个概念,全局解释器锁(GIL)。
Python代码的执行由Python虚拟机(解释器)来控制。Python在设计之初就考虑要在主循环中,同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在CPU中运行。同样地,虽然Python解释器可以运行多个线程,只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。在多线程环境中,Python虚拟机按照以下方式执行。
1.设置GIL。
2.切换到一个线程去执行。
3.运行。
4.把线程设置为睡眠状态。
5.解锁GIL。
6.再次重复以上步骤。
引子:
import threading
n = 0
def task1():
global n
for i in range(1000000):
n += 1
print('-----> task1中的n值是:', n)
def task2():
global n
for i in range(1000000):
n += 1
print('-----> task2中的n值是:', n)
if __name__ == '__main__':
th1 = threading.Thread(target=task1)
th2 = threading.Thread(target=task2)
th1.start()
th2.start()
th1.join()
th2.join()
print('n:', n)

这个问题,就是线程异步的问题,CPU轮流执行各个线程出现的问题,因此需要一个锁,即线程同步(出现执行速度慢的问题)
只要有共享数据,就要使用线程同步
Python底层只要用线程,默认加锁(GIL),只要有锁,就没办法达到真正的多线程
Python底层针对只要运算量达到某个程度,就自动释放GIL,因此出现的n最后的值不是2百万
在数据共享方面进程与线程的区别:
进程是每个进程中都有一份
线程是共用一个数据 ---》数据安全性问题
进程:一般使用于计算密集型(计算量大的,快速计算的)
线程:一般耗时操作的时候用,如下载,爬虫,IO操作
import threading
import time
lock = threading.Lock()
lyst = [0] * 10
def task1():
# 获取线程锁,如果已经加上锁,则等待锁的释放
lock.acquire() # 阻塞
for i in range(len(lyst)):
lyst[i] = 1
time.sleep(0.5)
lock.release() # 释放锁,只要不释放,其他的线程都无法进入运行状态
def task2():
lock.acquire() # 请求得到锁
for i in range(len(lyst)):
print('---->', lyst[i])
time.sleep(0.5)
lock.release()
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t2.start()
t1.start()
t2.join()
t1.join()
print(lyst)

3.死锁
开发过程中使用线程,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源,并且同时等待对方的资源,就会造成死锁。
尽管死锁很少发生,但一旦发生就会造成应用的停止响应,程序不做任何事情
一定要避免出现死锁,解决方式:
1.重构代码
2.在acquire(timeout=5)加入timeout,只要超时,就将锁释放
from threading import Thread, Lock
import time
lockA = Lock()
lockB = Lock()
class MyThread(Thread):
def run(self): # start
if lockA.acquire(): # 如果可以获取到锁则返回True
print(self.name+'获取了A锁') # 系统默认name时Thread-1
time.sleep(0.1)
if lockB.acquire(): # B锁已经被thread1拿了,因此就会阻塞
print(self.name+'又获取了B锁,原来还有A锁')
lockB.release()
lockA.release()
class MyThread1(Thread):
def run(self): # start
if lockB.acquire(): # 如果可以获取到锁则返回True
print(self.name+'获取了B锁') # 系统默认name时Thread-2
time.sleep(0.1)
if lockA.acquire():
print(self.name+'又获取了A锁,原来还有B锁')
lockA.release()
lockB.release()
if __name__ == '__main__':
th = MyThread()
th1 = MyThread1()
th.start()
th1.start()

解决
from threading import Thread, Lock
import time
lockA = Lock()
lockB = Lock()
class MyThread(Thread):
def run(self): # start
if lockA.acquire(): # 如果可以获取到锁则返回True
print(self.name+'获取了A锁') # 系统默认name时Thread-1
time.sleep(0.1)
if lockB.acquire(timeout=3): # B锁已经被thread1拿了,因此就会阻塞
print(self.name+'又获取了B锁,原来还有A锁')
lockB.release()
lockA.release()
class MyThread1(Thread):
def run(self): # start
if lockB.acquire(): # 如果可以获取到锁则返回True
print(self.name+'获取了B锁') # 系统默认name时Thread-2
time.sleep(0.1)
if lockA.acquire(timeout=3):
print(self.name+'又获取了A锁,原来还有B锁')
lockA.release()
lockB.release()
if __name__ == '__main__':
th = MyThread()
th1 = MyThread1()
th.start()
th1.start()
