不定期分享一些工作學習中遇到的問題與解決方法,歡迎關注,如果有問題請在下方留言
之前在寫多線程與多進程的時候,因為一般情況下都是各自完成各自的任務,各個子線程或者各個子進程之前并沒有太多的聯系,如果需要通信的話我會使用隊列或者數據庫來完成,但是最近我在寫一些多線程與多進程的代碼時,發現如果它們需要用到共享變量的話,需要有一些注意的地方
多線程之間的共享數據
標準數據類型在線程間共享
看以下代碼
這里我創建一個全局的int變量d,它的值是5,當我在5個線程中調用test函數時,將d作為參數傳進去,那么這5個線程所擁有的是同一個d嗎?我在test函數中通過id(data) 來打印一下它們的ID,得到了如下的結果
從結果中可以看到,在5個子線程中,data的id都是1763791776,說明在主線程中創建了變量d,在子線程中是可以共享的,在子線程中對共享元素的改變是會影響到其它線程的,所以如果要對共享變量進行修改時,也就是線程不安全的,需要加鎖。
自定義類型對象在線程間共享
如果我們要自定義一個類呢,將一個對象作為變量在子線程中傳遞呢?會是什么效果呢?
這里我定義一個簡單的類,在主線程初始化了一個該類型的對象d,然后將它作為參數傳給子線程,主線程和子線程分別打印了這個對象的id,我們來看一下結果
我們看到,在主線程和子線程中,這個對象的id是一樣的,說明它們用的是同一個對象。
無論是標準數據類型還是復雜的自定義數據類型,它們在多線程之間是共享同一個的,但是在多進程中是這樣的嗎?
多進程之間的共享數據
標準數據類型在進程間共享
還是上面的代碼,我們先來看一下int類型的變量在子進程間的共享
得到的結果是
可以看到它們的id是一樣的,說明用的是同一個變量,但是當我嘗試把d由int變為了string時,發現它們又不一樣了……
于是我又嘗試了list、Tuple、dict,結果它們都是不一樣的,我又回過頭來試著在多線程中使用列表元組和字典,結果在多線程里它們的id還是一樣的。
這里有一個有趣的問題,如果是int類型,當值小于等于256時,它們在多進程間的id是相同的,如果大于256,則它們的id就會不同了,這個我沒有查看原因。
自定義類型對象在進程間共享
得到的結果是
可以看到它們的id是不同的,也就是不同的對象。
在多進程間如何共享數據
我們看到,數據在多進程間是不共享的(小于256的int類型除外),但是我們又想在主進程和子進程間共享一個數據對象時該如何操作呢?
在看這個問題之前,我們先將之前的多線程代碼做下修改
我們這個代碼的目的是這樣,使用自定義的Data類型對象,當經過5個子線程操作以后,每個子線程對其data值進行加1操作,最后在主線程打印對象的data值。
該輸出結果如下
可以看到在主線程最后打印出來了5,符合我們的預期,但是如果放到多進程中呢?因為多進程下,每個子進程所持有的對象是不同的,所以每個子進程操作的是各自的Data對象,對于主進程的Data對象應該是沒有影響的,我們來看下它的結果
它的輸出結果是:
最后的輸出是0,說明了子進程對于主進程傳入的Data對象操作其實對于主進程的對象是不起作用的,我們需要怎樣的操作才能實現子進程可以操作主進程的對象呢?我們可以使用multiprocessing.managers 下的 BaseManager 來實現
使用from multiprocessing.managers import BaseManager 引入 BaseManager以后,在定義完Data類型之后,使用BaseManager.register("mydata",Data) 將Data類型注冊到BaseManager中,并且給了它一個名字叫mydata,之后就可以使用BaseManager對象的這個名字來初始化對象,我們來看一下輸出
我們看到,雖然在每個子進程中使用的是不同的對象,但是它們的值卻是可以“共享”的。
標準的數據類型也可以通過multiprocessing庫中的Value對象,舉一個簡單的例子
這里使用d = multiprocessing.Value("l",10) 初始化了一個數字類型的對象,這個類型是Synchronized wrApper for c_long ,multiprocessing.Value在初始化時,第一個參數是類型,第二個參數是值,具體支持的類型如下
還可以使用ctypes庫里和類初始化字符串
>>> from ctypes import c_char_p >>> s = multiprocessing.Value(c_char_p, b'\xd1\xee\xd1\xe5\xd0\xc7') >>> print(s.value.decode('gbk')) 楊彥星
ctypes里的類型與Python的對應關系如下:
還可以使用Manager對象初始list,dict等
其實我們這里所說的共享只是數據值上的共享,因為在多進程中,各自持有的對象都不相同,所以如果想要同步狀態需要另辟蹊徑。不過這種在自己寫的小項目中可以簡單的使用,如果做一些大一點的項目,還是建議不要使用這種共享數據的方式,這種大大的增加了程序間的耦合性,使用邏輯變得復雜難懂,所以建議還是使用隊列或者數據庫作為進程間通信的渠道。
參考文章
Python 進程之間共享數據(全局變量)
Python多進程編程-進程間共享數據