那我们应该如何解决这个导致多线程处理变量的多线程问题呢?锁定! Z
'''
import threading
n = 10
def child():
global n
i = 1000000
while i > 0:
n += 1
n -= 1
i -= 1
t1 = threading.Thread(target=child)
t2 = threading.Thread(target=child)
t3 = threading.Thread(target=child)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print(n)
'''
import threading
import time
start = time.time()
n = 10
tlock = threading.Lock()
def child():
global n
i = 1000000
while i > 0:
i -= 1
tlock.acquire()
try:
n += 1
n -= 1
finally:
tlock.release()
t1 = threading.Thread(target=child)
t2 = threading.Thread(target=child)
t3 = threading.Thread(target=child)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
end = time.time()
print(n)
print('消耗的时间为{0}秒'.format(end-start)) # 消耗了14秒
此执行的结果为10,因为每次执行n的加法和减法都会添加一个锁. 当任何线程执行加减运算时,如果其他线程不释放锁,则执行无法继续. 等待释放锁以继续执行. 但是比较消耗的时间,使用锁的时间要比不使用锁的时间长得多. 锁确保执行键代码时,只有一个线程可以执行该锁,而下一个线程可以继续获取该锁以继续执行. 缺点是它组织多线程并发执行,并且锁中的代码执行在单个线程中执行. 我们只在这里添加了一把锁. 如果存在多个锁,则可能导致多个线程争用该锁,从而导致死锁,并且该线程挂起并且无法继续执行.
关于GIL,让我们先来看一个示例
import threading
def thread():
m = 10
while True: # 死循环
m += 1
m -= 1
if __name__ == '__main__':
for i in range(10):
t = threading.Thread(target=thread)
t.start()
让我们看一下资源占用情况. 我们已经打开了10个流程. 为什么此过程不占用所有CPU使用率? (作者的计算机是4核i5处理器). 因为这是由于GIL的存在.

GIL(全局解释锁)实际上不是Python问题,而是CPython解释器.
在执行任何Python线程之前,必须获取GIL锁,然后每执行100个字节的字节码,解释器就会释放GIL锁,从而使其他线程有机会执行. 这个GIL全局锁实际上锁定了所有线程执行的代码. 因此,多线程在Python中交替执行. 即使在100核CPU上运行100个线程,也只能使用一个核. -来自“廖雪峰网站”
每个Python程序都有至少一个线程,即主线程. 要创建更多线程,我们必须导入线程模块并使用它创建所需数量的其他线程. 创建线程有两种方法:
1. 调用threading.Thread()并将其传递给可调用对象; 2.创建threading.Thread的子类. 子类化是最灵活的方法,它也非常简单. 子类可以重新实现__init __()方法(在这种情况下,必须调用基类的实现),并可以重新实现run()方法-进程的工作在此方法中完成. 应当注意,我们的代码绝不能调用run()方法-线程是通过调用start()方法启动的,该方法在适当的时候内部调用run()方法.
第一种方法将不在此处详细描述. 之前有详细的示例. 在这里,我们解释threading.Thread子类的调用方法:
守护进程线程
import threading
import time
def a():
time.sleep(2)
print('正在执行程序:a')
def b():
time.sleep(5)
print('正在执行程序:b')
t1 = threading.Thread(target=a)
t2 = threading.Thread(target=b)
t2.daemon = True # 守护线程守护的是非守护线程,其他非守护线程执行完成执行
t1.start()
t2.start()
print('当前是主线程')
当前是主线程
正在执行程序:a
t2作为守护程序线程,我们可以看到b线程尚未完成执行,守护程序线程是确保非守护程序线程执行完成. 守护程序线程用于其他非守护程序线程. 当非守护程序线程完成运行时,守护程序线程结束. 守护线程具有受害者哈哈哈的无畏精神. 程中还可以看到t2.join()方法在继续执行主线程之前等待子线程的执行完成.
import threading
from queue import Queue
class Thread2(threading.Thread):
def __init__(self, workers):
super().__init__()
self.workers = workers
def run(self):
while True:
worker = self.workers.get()
print('get的数据:{0}'.format(worker))
self.workers.task_done()
q = Queue()
t = Thread2(q)
t.daemon = True # 守护线程
t.start()
for i in range(20):
q.put(i) # put一个,子线程就开始消费
q.join() # queue.join()一般和task_down()是一对儿,join表示任务未完成挂起;task_down()则会阻塞join()
print('结束了')
这个例子很有趣. 主线程产生(写队列),而子线程消耗(读队列). 当队列因join()而挂起时,主线程被阻塞. 当队列耗尽时,join()块将失败,并且主线程的代码将继续执行.
与使用进程进行通信等多进程相比,多线程通信相对容易,但是由于Cpython解释器的存在,多线程难以调度多个处理器,并且多线程内存是共享的,因此一旦线程有问题,整个过程可能会崩溃;多进程创建进程的消耗将更长(请参阅进程池Pool),并且计算机同时调度的进程数将受到限制(受内存和CPU等因素影响). 因此,在选择多线程和多进程时,需要考虑更多的应用场景.
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/ruanjian/article-288253-2.html
希望下一代能做好
晚安