2015-09-28 60 views
19

在我的應用程序中,通過發出請求來更改公共對象的狀態,並且響應取決於狀態。全局變量是否在燒瓶中安全?

class SomeObj(): 
    def __init__(self, param): 
     self.param = param 
    def query(self): 
     self.param += 1 
     return self.param 

global_obj = SomeObj(0) 

@app.route('/') 
def home(): 
    flash(global_obj.query()) 
    render_template('index.html') 

如果我在我的開發服務器上運行這個,我期望得到1,2,3等等。如果同時請求100個不同的客戶端,是否會出現問題?預期的結果是,100個不同的客戶端都會看到1到100之間的唯一數字。或者會發生類似的情況:

  1. 客戶端1查詢。 self.param由1
  2. 遞增可以執行return語句之前,線程切換到客戶端2. self.param再次增加。
  3. 線程切換回客戶機1,並且客戶端返回的數字2,說。
  4. 現在線程移動到客戶端2,返回他/她的號碼3

由於只有兩個客戶,預期的結果是1和2,而不是2和3的一些被跳過。

會我擴大我的應用程序這實際上發生的呢?我應該看一下全球變量的其他選擇嗎?

回答

20

不能使用全局變量來保存這類數據。它不僅是不是線程安全的,它不是工藝在生產中產卵多個進程安全,WSGI服務器。如果您使用線程來處理請求,那麼您的計數不僅會出錯,還會因處理請求的進程而異。

使用數據源燒瓶以外舉行的全球數據。數據庫,memcached或redis都是合適的獨立存儲區域,具體取決於您的需要。您也可以將會話用於每個用戶的簡單數據。


開發服務器爲單線程,默認爲單進程。你不會看到你描述的行爲,因爲每個請求都將被同步處理。啓用線程或進程,你會看到它。 app.run(threaded=True)app.run(processes=10)


某些WSGI服務器可能支持gevent或其他異步工作者。全局變量仍然不是線程安全的,因爲大多數競態條件仍然沒有保護。您仍然可以有一個場景,其中一個工作人員獲取價值,收益,另一個工人修改它,收益,然後第一個工作人員也修改它。

-1

一個快速和骯髒,非線程安全的機制:

app = Flask(__name__) 
    app.speed = 0.0 # add new properties to the instance 
    app.last_check_tm = time.time() 
    ... 

我有哪裏Fask正在作爲一個樹莓派一個本地WiFi服務器的應用程序,允許用戶用手機進行連接。應用程序需要「記住」數據以及請求之間的時間,並使用此信息收集本地硬件輸入信息(其中session不可用)。注意,這只是Flask一次只能服務一個用戶的任何用途。

編輯。正如下面指出的,這種方法是一個壞主意。sqlite有一個選項來創建內存中的數據庫,防止重複寫入SD卡(這正是我試圖避免的) PS令人遺憾的sqlite不能運行在不同的線程,所以這個簡單的解決方案可能不會工作我和我將需要一個更復雜的系統(可能是在不同的子處理中運行Fask的一些東西。處理和發送Queue對象的信息,有點麻煩!)正如大衛主義指出,sqlite可以做到這一點我已經交換了這個更好的方法。創建連接使用conn = sqlite3.connect(':memory:',check_same_thread=False)

+1

這似乎不回答問題。另外,當Flask 1.0發佈時,它將不再安全工作,默認情況下它會切換到啓用線程。不要這樣做。如果您需要輕量級和本地可用的東西,請使用SQLite數據庫。 – davidism

+0

@davidism謝謝我錯了關於sqlite3的限制。我也考慮完全回答我的答案。正如你所說,它根本不回答OP。然而,經過相當多的谷歌搜索這是我發現的最相關和有用的信息,所以我認爲這可能會在某個階段幫助某人。因爲Stackoverflow已經成爲* all *編程答案的第一位來源,所以這些問題必須根據谷歌查詢來解釋,而不是OP的實際措辭! – paddyg

+0

以「非線程安全機制」開頭的「是全局變量線程安全」的答案顯然不是對問題的回答,而且實際上並沒有幫助。如果你想自我回答一個關於如何在Raspberry Pi上使用SQLite或單線程應用的問題,那就不要把它放在一個無關的問題上。 – davidism