简体   繁体   中英

Is it safe to use global variables in flask?

let's say I want to have a flask server running with same global state that might be modified through a request. That is, let the initial state be represented by the number 5 . By calling /getn , the state is returned and through /inc it is increased.

from flask import Flask
from flask import request
import time
import os
app = Flask(__name__)

state = 5

@app.route('/inc')
def inc():
    global state
    print("sleep")
    time.sleep(5)
    state += 1
    return 'done'

@app.route('/getn')
def getn():
    global state
    return f"{state}"



if __name__ == '__main__':
    app.run(threaded=True, host="0.0.0.0")

Now I'm calling curl http://127.0.0.1:5000/inc from two different terminals at the same time. After reading through same blogs, I expected that after both calls were done, /getn would give me the number 6 , as globals are assumed to be not thread safe in flask. However, the returned state equals 7 .

Can so explain this? Furthermore, what would be the correct way to implement this task?

Thanks!

As I understand it, when you are running Flash threaded you have a potential issue with the statement state += 1 where state is a global since that statement when converted to bytecode becomes:

0 LOAD_GLOBAL              0 (state)
2 LOAD_CONST               1 (1)
4 INPLACE_ADD
6 STORE_GLOBAL             0 (state)

Let's assume state is initially 5. Now what would happen if the first thread executes the first three instructions above and computes a new value of 6 but before it has a chance to store this new value back out to state with the fourth instruction, the thread loses control to the second thread? The second thread therefore loads the same value of 5 from state , re-computes a new value of state , namely 6, and stores it out. Now when the first thread regains control it gets to execute the fourth bytecode instruction and stores out 6 again.

The problem, therefore, is that the += operator is not atomic , ie it is implemented as a sequence of Python bytecode instructions and if a thread gets interrupted between the 3rd and 4th instruction to another thread that is executing the same operation that completes , then one of the updates will be lost. But the probability of this occurring is not that high with threading since multiple threads do not execute bytecode in parallel since they must lock on the Global Interpreter Lock (GIL) and only lose control to another thread when their time slices have expired or when they go into an I/O wait, which does not apply here.

But to be perfectly safe, you can and should do the update under control of a lock.

from flask import Flask
from flask import request
import time
import os
from threading import Lock


app = Flask(__name__)

state_lock = Lock()
state = 5

@app.route('/inc')
def inc():
    global state
    print("sleep")
    time.sleep(5)
    with state_lock:
        state += 1
    return 'done'

@app.route('/getn')
def getn():
    # The global statement is not really required
    # since state is read/only, but it's okay:
    global state
    return f"{state}"



if __name__ == '__main__':
    app.run(threaded=True, host="0.0.0.0")

Here is a better example to show that incrementing a global is not thread safe. If you run two concurrent invocations of the /inc endpoint and the invoke the /getn endpoint, the output should be 200000000 but now there is a very good chance that a thread will lose its time slice one or more times when it matters and the result will not be correct. Then retry it with the incrementing done within a with state_lock: block.

from flask import Flask
from threading import Lock


app = Flask(__name__)

state_lock = Lock()
state = 0

@app.route('/inc')
def inc():
    global state
    for _ in range(100_000_000):
        state += 1
    return 'done'

@app.route('/getn')
def getn():
    return f"{state}"



if __name__ == '__main__':
    app.run(threaded=True, host="0.0.0.0")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM