Python多线程,多进程摘要(2)

电脑杂谈  发布时间:2020-07-26 15:02:21  来源:网络整理

python多进程效率_python 获取进程pid_python 进程 daemon

那我们应该如何解决这个导致多线程处理变量的多线程问题呢?锁定! 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的存在.

img.png

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

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...