Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/358.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 烧瓶里有蟒蛇吗?_Python_Multithreading_Flask - Fatal编程技术网

Python 烧瓶里有蟒蛇吗?

Python 烧瓶里有蟒蛇吗?,python,multithreading,flask,Python,Multithreading,Flask,我最近正在研究python多线程,我发现GIL将迫使python一次运行一个线程,即使在多核CPU上也是如此 所以我做了一些小手术 我的代码如下: import threading import time COUNT = 50000000 def count(): i = 0 print('thread id =', threading.get_ident()) while(i < COUNT): i = i +1 start_time = ti

我最近正在研究python多线程,我发现GIL将迫使python一次运行一个线程,即使在多核CPU上也是如此

所以我做了一些小手术

我的代码如下:

import threading
import time

COUNT = 50000000
def count():
    i = 0
    print('thread id =', threading.get_ident())
    while(i < COUNT):
        i = i +1

start_time = time.time()
count()
count()
end_time = time.time()
print(f'execution time without multiple threading : {end_time - start_time}')

start_time = time.time()
t_1 = threading.Thread(target=count)
t_2 = threading.Thread(target=count)
t_1.start()
t_2.start()
t_1.join()
t_2.join()
end_time = time.time()
print(f'execution time with multiple threading : {end_time - start_time}')
这就是吉尔的工作原理

但现在我在烧瓶上做同样的过程 看来吉尔并没有像预期的那样工作。 这是我的代码:

server.py

from flask import Flask
import threading
import os

app = Flask(__name__)
COUNT = 50000000

@app.route('/')
def hello():
    print ('Thread id = ', threading.get_ident())
    print ('Process id = ', os.getpid())
    i = 0
    while(i < COUNT):
        i = i +1
    return "Hello World!"

if __name__ == '__main__':
    app.run()
import requests
import time
import multiprocessing as mp

def send():
    start_time = time.time()
    r = requests.get('http://localhost:5000')
    end_time = time.time()
    print(f'response after {end_time - start_time}')

if __name__ == '__main__':
    p_1 = mp.Process(target=send)
    p_2 = mp.Process(target=send)
    p_1.start()
    p_2.start()
    p_1.join()
    p_2.join()
server.py的结果:

Thread id =  18428
Process id =  19000
Thread id =  17436
Process id =  19000
127.0.0.1 - - [14/Sep/2020 09:36:19] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [14/Sep/2020 09:36:19] "GET / HTTP/1.1" 200 -
client.py的结果

response after 7.361770153045654
response after 7.373677015304565
似乎有两个客户同时得到了回复,但对于GIL,我的预期结果应该是
7.3第一个响应和14.xxx第二个响应。有人能帮我验证这个问题吗?

正如@ShadowRanger在评论中指出的那样,这个答案背后的核心原则(即两个进程对同一个变量倒计时,需要线程本地存储)是不正确的。但是,我将保留答案,因为它确实包含一些对OP找到解决方案有用的信息

使用GIL,Python解释器确实一次只允许运行一个线程——除了I/O操作。它有一个“切换间隔”,即它停止运行一个线程并查看运行另一个线程一段时间的频率。您可以通过以下方式进行检查:

sys.getswitchinterval()
在我的系统上设置为5ms

您看到的是两个线程,两个线程一次运行大约5ms,但都在递增相同的变量,因为您有一个与线程共享的地址空间。因此,没有两个线程分别计数到50000000,有两个线程分别计数到单个50000000的一部分

您需要的是“线程本地存储”,因此每个线程都会增加自己的计数器:

#!/usr/bin/env python3

from flask import Flask
import threading
import os, sys

app = Flask(__name__)
COUNT = 50000000

@app.route('/')
def hello():
    print ('Thread id = ', threading.get_ident())
    print ('Process id = ', os.getpid())
    mydata = threading.local()
    mydata.i = 0
    while(mydata.i < COUNT):
        mydata.i = mydata.i +1
    return "Hello World!"

if __name__ == '__main__':

    print(f'switch interval: {sys.getswitchinterval()}')
    app.run()
如果我在线程本地存储中使用
I
运行代码,我会得到:

response after 19.429346084594727 
response after 19.553659200668335

正如@ShadowRanger在评论中指出的,这个答案背后的核心原则(即两个进程对同一个变量倒计时,并且需要线程本地存储)是不正确的。但是,我将保留答案,因为它确实包含一些对OP找到解决方案有用的信息

使用GIL,Python解释器确实一次只允许运行一个线程——除了I/O操作。它有一个“切换间隔”,即它停止运行一个线程并查看运行另一个线程一段时间的频率。您可以通过以下方式进行检查:

sys.getswitchinterval()
在我的系统上设置为5ms

您看到的是两个线程,两个线程一次运行大约5ms,但都在递增相同的变量,因为您有一个与线程共享的地址空间。因此,没有两个线程分别计数到50000000,有两个线程分别计数到单个50000000的一部分

您需要的是“线程本地存储”,因此每个线程都会增加自己的计数器:

#!/usr/bin/env python3

from flask import Flask
import threading
import os, sys

app = Flask(__name__)
COUNT = 50000000

@app.route('/')
def hello():
    print ('Thread id = ', threading.get_ident())
    print ('Process id = ', os.getpid())
    mydata = threading.local()
    mydata.i = 0
    while(mydata.i < COUNT):
        mydata.i = mydata.i +1
    return "Hello World!"

if __name__ == '__main__':

    print(f'switch interval: {sys.getswitchinterval()}')
    app.run()
如果我在线程本地存储中使用
I
运行代码,我会得到:

response after 19.429346084594727 
response after 19.553659200668335

Python烧瓶中也有一个GIL 在我问的问题中,我没有单独向服务器发送请求。 实际上,一个单独的请求需要一半的时间才能得到响应。
为什么我们能同时得到两个答案?这是因为其中包含sys.getswitchinterval(),因此线程将每隔0.005秒切换一次,直到它们完成任务。

Python Flask中也有一个GIL 在我问的问题中,我没有单独向服务器发送请求。 实际上,一个单独的请求需要一半的时间才能得到响应。
为什么我们能同时得到两个答案?这是因为其中包含sys.getswitchinterval(),因此线程将每隔0.005秒切换一次,直到它们完成任务。

很可能flask(或运行flask的任何程序)正在将新进程分支到服务请求。GIL会伤害线程,但独立进程不会受到影响。GIL不会永远阻止线程。仍然有可能两个线程似乎同时运行,但实际上正在快速切换控制。@klauds.:在这种情况下不是;这里的延迟完全是CPU限制的循环(不是一个阻塞任务,其中一个可以工作,而另一个被阻塞)。每个线程完成的工作不能同时完成,它们必须在每次切换控件的基础上支付上下文切换的费用。如果工作是
时间.sleep(7)
或其他什么,它可以像您描述的那样工作,但是如果一个线程在纯CPU限制的工作上花费7秒,那么添加另一个线程再次执行相同的工作将使时间大约加倍。Uhg。。移动滚动失败。@ShadowRanger我想我只是找到了答案,我试着只发送一次请求,这需要一半的时间,所以这意味着GIL确实也在烧瓶上工作!!!!!!!很有可能flask(或者运行flask的任何东西)正在将新的流程分支到服务请求。GIL会伤害线程,但独立进程不会受到影响。GIL不会永远阻止线程。仍然有可能两个线程似乎同时运行,但实际上正在快速切换控制。@klauds.:在这种情况下不是;这里的延迟完全是CPU限制的循环(不是一个阻塞任务,其中一个可以工作,而另一个被阻塞)。每个线程完成的工作不能同时完成,它们必须在每次切换控件的基础上支付上下文切换的费用。如果工作是
时间.sleep(7)
或其他什么,它可以像您描述的那样工作,但是如果一个线程在纯CPU限制的工作上花费7秒,那么添加另一个线程再次执行相同的工作将使时间大约加倍。Uhg。。移动滚动失败。@ShadowRanger我想我只是找到了答案,我试着只发送一次请求,这需要一半的时间,所以这意味着GIL确实也在烧瓶上工作!!!!!!!这并不能解释它
i
是函数的局部变量,而不是全局变量,因此它不在线程之间共享。只有
COUNT
是全局的/共享的,并且它永远不会变异。@Shad