概念
- 线程又被称之为轻量级进程
- 一个进程可拥有多个并行的线程
- 通俗的一点说就是一个程序有多个任务可以同时进行
- 一个进程中线程共享相同的内存单元/内存地址空间,可以访问相同的成员
- 他们从同一个堆中分配对象,通讯、数据交换,同步操作
- 由于线程间的通信是在同一地址空间进行的,所以,不需要额外的通讯机制
- 通过以上等优势来提高Python的速度
进程与线程的区别
- 进程是系统进行资源分配和调度的独立单位
- 进程在执行过程中拥有独立的内存单元,多个线程共享,从而提高效率
- 线程是一个进程的实体,是CPU调度和分配的基本单位,从而提高程序运行效率
- 线程基本不拥有系统资源,只拥有运行时的一点必不可少的资源,但他与其他线程共享进程中的所有资源
- 使用多线程程序并发比较高
- 线程不能独立执行,必须依赖进程
- 线程执行开销小,但是不利于资源的管理保护
涉及到的模块
- from threading import Thread
- import time
创建线程
引入模块
1.import time
2.from threading import Thread
创建线程
1.创建一个执行函数
2.判断是否主动执行
3.创建单个进程或者循环创建出多个线程
4.线程开始执行
5.实例1:
// An highlighted block
from threading import Thread
import time
def xc():
print("当前线程执行了")
if __name__ == '__main__':
for i in range(5):
time.sleep(i)
t = Thread(target=xc)
t.start()
6.实例2:
// An highlighted block
from threading import Thread
import time
def xc(num):
print("当前线程执行了%d"%num)
if __name__ == '__main__':
for i in range(5):
time.sleep(i)
t = Thread(target=xc,args=(i+1,))
t.start()
7.实例3:
// An highlighted block
from threading import Thread
import time
def sing():
for i in range(3):
print("我正在唱歌")
t = Thread(target=sing)
t.start()
线程子类化
- 将线程封装到一个类中
- 对数据进行封装
- 实例1:
// An highlighted block
from threading import Thread
import time
class MyThread(Thread):
def run(self):
for i in range(10):
time.sleep(2)
string = "My name is "+self.name+" @ "+str(i)
print(string)
m = MyThread()
m.start()
- 实例2:
// An highlighted block
from threading import Thread
import time
class MyThread(Thread):
def run(self):
for i in range(10):
time.sleep(2)
string = "my name is "+self.name+" @ "+str(i)
print(string)
for i in range(2):
t = MyThread()
t.start()
线程的状态
- 阻塞 : 多线程的顺序是不规律的,当执行到sleep语句的时候,线程将被阻塞
- 就绪 : 当sleep语句结束后,程序将要执行,这时线程处在就绪的状态,等待调度,而线程调度会自行选择一个线程执行
- 运行 : 调度处线程之后,程序执行,这个状态就是运行状态
- 运行时可能会出现阻塞事件
全局变量
概念
- 在一个进程内,所有线程共享全局变量
- 能够在其他地方都不实用的环境中完成线程间的数据共享
- 缺点 : 线程可以对全局变量随意篡改,可能造成多线程间的全局变量混乱
共享
- 在线程的操作中,全局变量在线程中是共享的
实例1:
// An highlighted block
from threading import Thread
import time
g_num = 100
def worker1():
global g_num
for i in range(3):
#重新定义了g_num 所以g_num如果不声明为全局变量 他就是局部变量
g_num += 1
print("worker1里面的g_num等于%d"%g_num)
def worker2():
print("worker2里面的g_num等于%d"%g_num)
print("主线程的g_num等于%d"%g_num)
t1 = Thread(target=worker1)
t1.start()
time.sleep(2)
print('')
t2 = Thread(target=worker2)
t2.start()
实例2(反例,传参):
// An highlighted block
from threading import Thread
import time
g_num = 100
def worker1(num):
for i in range(3):
num += 1
print("worker1里面的g_num等于%d"%num)
def worker2(num):
print("worker2里面的g_num等于%d"%num)
print("主线程的g_num等于%d"%g_num)
t1 = Thread(target=worker1,args=(g_num,))
t1.start()
time.sleep(2)
print('')
t2 = Thread(target=worker2,args=(g_num,))
t2.start()
这个以传参的形式赋值,那么就不属于全局变量,结果不共享
线程同步
概念
- 当多线程同时修改某一个数据的时候,需要进行同步控制
- 线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁
- 互斥锁保证每次只有一个线程进行写入操作,从而保证多线程的情况下数据的正确性
- 某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改,直到该线程释放资源,将资源的状态变成“非锁定”,其他线程才能再次锁定该资源
- threading模块中定义了Lock类,可以方便的处理锁定
需要引入的模块
- from threading import Thread
- from threading import Lock
- import time
同步控制
- 案例:
// An highlighted block
from threading import Thread
num = 0
def worker():
global num
for i in range(2000000):
num += 1
if __name__ == '__main__':
for x in range(2):
t = Thread(target=worker)
t.start()
print("这是线程的结果=======%d"%num)
多次变更,在两万六千次左右就会出现误差
- 原因:数据进行运算时,先从内存中读到寄存器中去,运算完毕后,再从寄存器中读取到内存中来。
- 创建锁 : mutex = threading.Lock()
- 锁定 : mutex.acquire([blocking])
1.如果设定blocking为True,则当前线程会阻塞,知道获取这个锁为止,如果没有设定,默认为True
2.如果设定blocking为False,则当前线程不会阻塞 - 释放 : mutex.release()
- 实例:
// An highlighted block
from threading import Thread,Lock
import time
num = 0
#创建互斥所
mutex = Lock()
def worker():
global num
for i in range(10000000):
mutexPlag = mutex.acquire(False)
if mutexPlag:
num += 1
mutex.release()
if __name__ == '__main__':
for i in range(2):
t = Thread(target=worker)
t.start()
t.join()
print(num)
- 死锁现象 : 如果将线程锁定,而没有释放,那么就会出现死锁现象