多线程和多进程最主要的区别在于:多进程中同一个变量,会被拷贝到每一个进程中,互不影响
而多线程中,所有的变量都是线程间共享的。所以,任意一个变量都可以被线程进行修改,原因就是在于线程中变量共享的。
代码实例
import threading
import time
name = 'lucky'
def fun1():
global name
name = '张三'
print(threading.current_thread().name, name)
time.sleep(2)
def fun2():
print(threading.current_thread().name, name)
if __name__ == '__main__':
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
t1.join()
t2.join()
print(name)
print('over')
# ---结果为---
# Thread-1 张三
# Thread-2 张三
# 张三
# over
!> 从结果可以看出线程之间的数据是共享的
- 代码实例
import threading
i = 1
def change_num(num):
global i
i += num
i -= num
def fun1():
for i in range(10000000):
change_num(6)
def fun2():
for i in range(10000000):
change_num(8)
if __name__ == '__main__':
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
t1.join()
t2.join()
print(i)
print('over')
# ---结果---
# 第一次执行
# fun1---------- -1181950
# fun2------- -2083066
# 第二次执行
# fun1---------- -183358
# fun2------- -944143
从上述代码可以看出,两个线程同时对同一数据进行读写就会产生数据的错乱。
解决办法:在进行写的时候保证只有一个线程
lock锁是线程模块中的一个类,有俩个主要的方法:acquire() 和release() ,当调用acquire()时,它进行锁定并阻塞执行,直到调用release() ,以防止数据损坏,因为一次只有一个线程进行访问资源。
-
作用
避免线程冲突
-
注意
- 当前的线程锁定以后,后面的线程会等待 (线程等待/线程阻塞)
- 需要release以后才能解锁恢复正常
- 不能重复锁定
- 代码实例
import threading, time
lock = threading.Lock()
i = 1
def fun1():
global i
if lock.acquire(): # 上锁
for x in range(1000000):
i += x
i -= x
lock.release() # 解锁
print('fun1----------', i)
def fun2():
global i
if lock.acquire(): # 上锁
for x in range(10000000):
i += x
i -= x
lock.release() # 解锁
print('fun2-------', i)
if __name__ == '__main__':
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
t1.join()
t2.join()
print(i)
- lock的简写
with lock
for x in range(10000000):
i += x
i -= x
print('fun2-------', i)
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁
from threading import Thread,Lock
import time
mutexA = Lock()
mutexB = Lock()
class MyThread(Thread):
def run(self):
self.task1()
self.task2()
def task1(self):
mutexA.acquire()
print('%s task1 get A' %self.name)
mutexB.acquire()
print('%s task1 get B' % self.name)
mutexB.release()
mutexA.release()
def task2(self):
mutexB.acquire()
print('%s task2 get B' % self.name)
time.sleep(1) # Thread-2 拿到执行权,执行get A出现死锁,此时thread2需要B锁,而thread1占用,与此同时,thread1需要A锁,thread2占用
mutexA.acquire()
print('%s task2 get A' % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = MyThread()
t.start()
-------------------输出
Thread-1 task1 get A
Thread-1 task1 get B
Thread-1 task2 get B
Thread-2 task1 get A # 出现死锁,整个程序被阻塞
解决方法:递归锁
在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。
直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁,二者的区别是:递归锁可以连续acquire多次,而互斥锁只能acquire一次
-
Rlock简单使用
from threading import Thread,RLock import time mutexA = mutexB = RLock() class MyThread(Thread): def run(self): self.task1() self.task2() def task1(self): mutexA.acquire() print('%s task1 get A' %self.name) mutexB.acquire() print('%s task1 get B' % self.name) mutexB.release() mutexA.release() time.sleep(1) # Thread-2 拿到执行权,,此时counter=0,thread2执行task1 def task2(self): mutexB.acquire() print('%s task2 get B' % self.name) mutexA.acquire() print('%s task2 get A' % self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t = MyThread() t.start() ------------------------输出 Thread-1 task1 get A Thread-1 task1 get B Thread-2 task1 get A Thread-2 task1 get B Thread-3 task1 get A Thread-3 task1 get B Thread-4 task1 get A Thread-4 task1 get B Thread-5 task1 get A Thread-5 task1 get B
-
rlock的简写
rlock = threading.RLock() with rlock: pass