在CPython的当前实现中,存在称为"GIL"或"Global Interpreter Lock"的对象.它本质上是一个互斥锁,可以防止两个Python线程同时执行Python代码.这可以防止两个线程破坏Python解释器的状态,但也可以防止多个线程真正一起执行.基本上,如果我这样做:
# Thread A
some_list.append(3)
# Thread B
some_list.append(4)
Run Code Online (Sandbox Code Playgroud)
我无法破坏列表,因为在任何给定时间,只有其中一个线程正在执行,因为它们必须持有GIL才能执行此操作.现在,列表中的项目可能会以某种不确定的顺序添加,但关键是列表没有损坏,并且总会添加两件事.
所以,现在到C#.C#基本上面临与Python相同的问题,那么,C#如何阻止这种情况呢?如果有人知道的话,我也有兴趣听Java的故事.
澄清:我对没有显式锁定语句的情况感兴趣,特别是对VM.我知道Java和C#都存在锁定原语 - 它们也存在于Python中:GIL不用于多线程代码,只是为了保持解释器的理智.我对以上的直接等价感兴趣,所以,在C#中,如果我能够记得足够...... :-)
List<String> s;
// Reference to s is shared by two threads, which both execute this:
s.Add("hello");
// State of s?
// State of the VM? (And if sane, how so?)
Run Code Online (Sandbox Code Playgroud)
这是另一个例子:
class A
{
public String s;
}
// Thread A & B
some_A.s = some_other_value;
// some_A's state must change: how does it change?
// Is the VM still in good …Run Code Online (Sandbox Code Playgroud) 我知道Python线程一次只能执行一个字节码,那么为什么线程库会提供锁?我假设如果一次只执行一个线程,则不会发生竞争条件.
该库提供锁,条件和信号量.这是同步执行的唯一目的吗?
更新:
我做了一个小实验:
from threading import Thread
from multiprocessing import Process
num = 0
def f():
global num
num += 1
def thread(func):
# return Process(target=func)
return Thread(target=func)
if __name__ == '__main__':
t_list = []
for i in xrange(1, 100000):
t = thread(f)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print num
Run Code Online (Sandbox Code Playgroud)
基本上我应该启动100k线程并递增1.返回的结果是99993.
a)如果有GIL同步和避免竞争条件,结果如何不是99999?b)甚至可以启动100k OS线程吗?
看到答案后更新2:
如果GIL没有真正提供一种方法来执行简单的操作,例如原子递增,那么将它放在那里的目的是什么?它对于讨厌的并发问题没有帮助,那么它为什么要到位呢?我听说过C扩展的用例,有人会举例说明吗?
据我所知,Python的线程库使用POSIX线程进行线程处理,并且它不能在多核上运行。那么我们是否有可能使用 Open MP 为 Python 线程实现多核线程系统呢?
我试图了解CPython的GIL如何工作以及CPython 2.7.x和CPython 3.4.x中GIL之间的区别是什么.我正在使用此代码进行基准测试:
from __future__ import print_function
import argparse
import resource
import sys
import threading
import time
def countdown(n):
while n > 0:
n -= 1
def get_time():
stats = resource.getrusage(resource.RUSAGE_SELF)
total_cpu_time = stats.ru_utime + stats.ru_stime
return time.time(), total_cpu_time, stats.ru_utime, stats.ru_stime
def get_time_diff(start_time, end_time):
return tuple((end-start) for start, end in zip(start_time, end_time))
def main(total_cycles, max_threads, no_headers=False):
header = ("%4s %8s %8s %8s %8s %8s %8s %8s %8s" %
("#t", "seq_r", "seq_c", "seq_u", "seq_s",
"par_r", "par_c", "par_u", "par_s"))
row_format = ("%(threads)4d …Run Code Online (Sandbox Code Playgroud) 我知道 GIL 会阻止 python 跨内核运行其线程。如果是这样,为什么在网络服务器中使用 python,youtube、instagram 等公司如何处理它。
PS:我知道像多处理这样的替代方法可以解决它。但是,如果有人可以将其与他们处理的场景一起发布,那就太好了。
我有一个 Flask 应用程序,在命令行上运行时可以正常工作,但是当它通过 uWSGI 运行时,它无法正确响应请求或工作线程无法正常工作。我重写了一个简单的概念验证/失败程序来演示这个问题:
from datetime import datetime
from threading import Event, Thread
from flask import Flask
class JobManager:
def __init__(self):
self.running = False
self.event = Event()
def start(self):
self.running = True
while self.running:
print("Processing Job at", datetime.now().strftime('%c'))
self.event.clear()
self.event.wait(5)
if self.event.is_set():
print("Interrupted by request!")
def stop(self):
self.running = False
self.event.set()
app = Flask(__name__)
jobs = JobManager()
t = Thread(target=jobs.start)
t.start()
@app.route('/')
def hello_world():
global jobs
jobs.event.set()
return "I'm alive at " + datetime.now().strftime('%c')
if __name__ == '__main__':
app.run() …Run Code Online (Sandbox Code Playgroud) 我正在研究并试图了解 python GIL 和在 python 中使用多线程的最佳实践。我找到了这个演示文稿和这个视频
我试图重现演示文稿前 4 张幻灯片中提到的奇怪和疯狂的问题。这个问题老师在视频中也提到过(前4分钟)。我写了这个简单的代码来重现问题
from threading import Thread
from time import time
BIG_NUMBER = 100000
count = BIG_NUMBER
def countdown(n):
global count
for i in range(n):
count -= 1
start = time()
countdown(count)
end = time()
print('Without Threading: Final count = {final_n}, Execution Time = {exec_time}'.format(final_n=count, exec_time=end - start))
count = BIG_NUMBER
a = Thread(target=countdown, args=(BIG_NUMBER//2,))
b = Thread(target=countdown, args=(BIG_NUMBER//2,))
start = time()
a.start()
b.start()
a.join()
b.join()
end = time()
print('With Threading: …Run Code Online (Sandbox Code Playgroud) 的文档concurrent.futures.ThreadPoolExecutor说:
改变在3.5版本中:如果max_workers是
None或者没有给出,将默认为机器上的处理器,乘以数量5,假设的ThreadPoolExecutor通常用于重叠I / O,而不是CPU的工作,工人的数量应该更高比ProcessPoolExecutor的工人数量。
我想了解为什么默认max_workers值取决于 CPU 的数量。不管我有多少 CPU,在任何时间点都只能运行一个 Python 线程。
让我们假设每个线程都是 I/O 密集型的,它只有 10% 的时间在 CPU 上,90% 的时间在等待 I/O。然后让我们假设我们有 2 个 CPU。我们只能运行 10 个线程来使用 100% 的 CPU。我们不能再使用 CPU,因为在任何时间点都只有一个线程在运行。即使有 4 个 CPU,也是如此。
那么为什么默认是max_workers根据 CPU 数量来决定的呢?
此代码创建了一个竞争条件:
import threading
ITERS = 100000
x = [0]
def worker():
for _ in range(ITERS):
x[0] += 1 # this line creates a race condition
# because it takes a value, increments and then writes
# some inrcements can be done together, and lost
def main():
x[0] = 0 # you may use `global x` instead of this list trick too
t1 = threading.Thread(target=worker)
t2 = threading.Thread(target=worker)
t1.start()
t2.start()
t1.join()
t2.join()
for i in range(5):
main()
print(f'iteration {i}. expected x …Run Code Online (Sandbox Code Playgroud) 有PEP-554和PEP-684。两者都旨在支持线程级别的多个解释器。
有谁知道这些 PEP 是否至少在 Python 的实验版本或预发布版本(如 3.11)中实现?
我发现 Python 3.10(甚至可能是 3.9)在实验版本中具有这些功能。如果您通过配置以下标志来构建 CPython:
./configure --with-experimental-isolated-subinterpreters
或者在编译所有文件时在编译命令中添加define .c:
#define EXPERIMENTAL_ISOLATED_SUBINTERPRETERS 1
我在一个著名的项目中发布了启用此功能的请求,请参阅此处的问题。
启用此功能后,我想我将能够在多个线程(而不是进程)内创建单独的解释器,这意味着我不再需要多重处理。
更重要的是,根据此功能描述,当使用多个解释器时,不需要有单个 GIL,单独线程中的每个解释器都有自己的 GIL。这意味着即使解释器是在线程内创建的,仍然会使用所有 CPU 核心,就像multiprocessing. 当前的 Python 遭受 GIL 的困扰只是因为它强制只使用单个 CPU 核心,因此multiprocessing人们使用所有 CPU 核心来克服这个问题。
在描述这些功能时,据说作者必须手动修改 1500 个静态和全局变量,将它们全部移动到线程状态结构内的每线程本地表中。
想必所有这些新功能现在只能通过Python C API来使用。
如果这里有人知道如何使用这些独立的子解释器功能,您能否提供一些 Python 代码或 C API 代码以及如何使用它们的详细示例?
具体来说,我感兴趣的是如何以使用所有CPU核心的方式使用解释器,即我想知道如何避免单个GIL,而是使用多个GIL(实际上是本地锁,LIL)。当然,我想要内部线程,而不使用multiprocessing.
gil ×10
python ×9
python-3.x ×2
c# ×1
cpu ×1
cpython ×1
django ×1
flask ×1
interpreter ×1
java ×1
locks ×1
performance ×1
pthreads ×1
python-2.7 ×1
python-c-api ×1
uwsgi ×1
web-services ×1