多线程,python中创建多线程模块是Threading模块,Python中Thread是比较底层的模块,Threading是对Thread模块的封装。
首先使用threading模块来创建线程;
import threading
def say():
print("-------子线程-------")
if __name__ == "__main__":
for i in range(5):
thread = threading.Thread(target=say) # 创建多线程实例,Thread中target是需要子线程执行的函数
thread.start() # 启动多线程
第二种方式,通过集成的方式来创建线程;
import threading
import time
class test_thread(threading.Thread): # 通过继承threading.Thread重写run方法的形式,创建子线程
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm " + self.name
print(msg)
def test():
for i in range(5):
t = test_thread() # 实例化线程对象
t.start()
if __name__ == "__main__":
test()
运行结果:
I'm Thread-5
I'm Thread-3
I'm Thread-4
I'm Thread-2
I'm Thread-1
共享全局变量
线程还有一个特点是,全局变量是共享的,不同于进程全局变量之间是不同享,想要使用被改变的变量,需要采取一定的措施(进程之间的通信),才能共享数据。
线程与进程的区别:
定义的不同
1.进程是系统进⾏资源分配和调度的⼀个独⽴单位.
2.线程是进程的⼀个实体,是CPU调度和分派的基本单位,它是⽐进程更⼩的 能独⽴运⾏的基本单位.线程⾃⼰基本上不拥有系统资源,只拥有⼀点在运 ⾏中必不可少的资源(如程序计数器,⼀组寄存器和栈),但是它可与同属⼀个进程的其他的线程共享进程所拥有的全部资源.
区别
⼀个程序⾄少有⼀个进程,⼀个进程⾄少有⼀个线程.
线程的划分尺度⼩于进程(资源⽐进程少),使得多线程程序的并发性⾼。
进程在执⾏过程中拥有独⽴的内存单元,⽽多个线程共享内存,从⽽极 ⼤地提⾼了程序的运⾏效率
线线程不能够独⽴执⾏,必须依存在进程中
优缺点
线程和进程在使⽤上各有优缺点:线程执⾏开销⼩,但不利于资源的管理和 保护;⽽进程正相反。
线程锁Lock
缺点 就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量 的混乱 (即线程⾮安全)怎么样解决线程非安全这个问题呢?这里就引出了线程锁的概念
当多个线程⼏乎同时修改某⼀个共享数据的时候,需要进⾏同步控制
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引⼊互 斥锁。
互斥锁为资源引⼊⼀个状态:锁定/⾮锁定。
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他 线程不能更改;直到该线程释放资源,将资源的状态变成“⾮锁定”,其他的 线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作, 从⽽保证了多线程情况下数据的正确性。
threading模块中定义了Lock类,可以⽅便的处理锁定;
from threading import Thread, Lock
import time
g_num = 0
def test1():
global g_num
# 这个线程和test2线程都在抢着 对这个锁 进行上锁,如果有1方成功的上锁,那么导致另外
# 一方会堵塞(一直等待)到这个锁被解开为止
mutex.acquire()
for i in range(1000000):
g_num += 1
mutex.release() # 用来对mutex指向的这个锁 进行解锁,,,只要开了锁,那么接下来会让所有因为
# 这个锁 被上了锁 而堵塞的线程 进行抢着上锁
print("---test1---g_num=%d" % g_num)
def test2():
global g_num
mutex.acquire()
for i in range(1000000):
g_num += 1
mutex.release()
print("---test2---g_num=%d" % g_num)
# 创建一把互斥锁,这个锁默认是没有上锁的
mutex = Lock()
p1 = Thread(target=test1)
p1.start()
p2 = Thread(target=test2)
p2.start()
print("---g_num=%d---" % g_num)
运行结果:
C:\Users\Admin\PycharmProjects\BlockChain\venv\Scripts\python.exe C:/Users/Admin/PycharmProjects/BlockChain/Thread/threadingtest.py
---g_num=172105---
---test1---g_num=1000000
---test2---g_num=2000000
总结:
锁的好处:
确保了某段关键代码只能由⼀个线程从头到尾完整地执⾏,这里就不会出现如果一个线程更改完数据后,没有生效时,而第二个线程再次更改数据,避免了多线程共享数据的出错。
锁的坏处:
阻⽌了多线程并发执⾏,包含锁的某段代码实际上只能以单线程模式执 ⾏,效率就⼤⼤地下降了 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对⽅持有 的锁时,可能会造成死锁
使用线程互斥锁是解决了多线程共享数据可能出差的问题,但是还有可能会造成死锁。
死锁
在线程间共享多个资源的时候,如果两个线程分别占有⼀部分资源并且同时 等待对⽅的资源,就会造成死锁。尽管死锁很少发⽣,但⼀旦发⽣就会造成应⽤的停⽌响应。下⾯看⼀个死锁 的例⼦。
import threading
import time
class MyThread1(threading.Thread):
def run(self):
if mutexA.acquire():
print(self.name + '----do1---up----')
time.sleep(1)
if mutexB.acquire(): # mutexB在等待MyTherad2中 mutexBjeisu
print(self.name + '----do1---down----')
mutexB.release() #释放 mutexB锁
mutexA.release() #释放 mutexA
class MyThread2(threading.Thread):
def run(self):
if mutexB.acquire():
print(self.name + '----do2---up----')
time.sleep(1)
if mutexA.acquire():# mutexA在等待MyTherad1中 mutexA解锁
print(self.name + '----do2---down----')
mutexA.release() # 释放mutexA锁
mutexB.release() # 释放mutexB锁
mutexA = threading.Lock()
mutexB = threading.Lock()
if __name__ == '__main__':
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()
运行结果:
C:\Users\Admin\PycharmProjects\BlockChain\venv\Scripts\python.exe C:/Users/Admin/PycharmProjects/BlockChain/Thread/threadingtest.py
Thread-1----do1---up----
Thread-2----do2---up----
通过上面的例子可以看出,双反都在等待对方锁的释放,执行结果就没有再往下执行,这就造成了死锁。程序会一直在处于等待的状态。
怎么样来避免这种死锁的发生?这里引入同步的概念
同步
同步就是让多个进程有序的执行,从而避免死锁的发生。例
from threading import Thread,Lock
from time import sleep
class Task1(Thread):
def run(self):
while True:
if lock1.acquire():
print("------Task 1 -----")
sleep(0.5)
lock2.release()
class Task2(Thread):
def run(self):
while True:
if lock2.acquire():
print("------Task 2 -----")
sleep(0.5)
lock3.release()
class Task3(Thread):
def run(self):
while True:
if lock3.acquire():
print("------Task 3 -----")
sleep(0.5)
lock1.release()
#使用Lock创建出的锁默认没有“锁上”
lock1 = Lock()
#创建另外一把锁,并且“锁上”
lock2 = Lock()
lock2.acquire()
#创建另外一把锁,并且“锁上”
lock3 = Lock()
lock3.acquire()
t1 = Task1()
t2 = Task2()
t3 = Task3()
t1.start()
t2.start()
t3.start()
在创建锁时,将其他两把锁上锁,只让一个线程来执行,执行完成后接着把下一把锁解开,让三个线程有序的执行。