将字符串存储在多处理 sharedctypes 数组中
Storing strings in a multiprocessing sharedctypes Array
我正在尝试使用多处理模块的 sharedctypes 部分在进程之间共享一些字符串。
TL;DR:
我希望将我的字符串放入一个 sharedctypes 数组中,如下所示:
from multiprocessing.sharedctypes import Array
Array(ctypes.c_char, ['a string', 'another string'])
更多信息:
docs有这样的注释:
"请注意,ctypes.c_char 的数组具有值和原始属性,允许使用它来存储和检索字符串。"
单独使用c_char
:
from multiprocessing.sharedctypes import Array
Array(ctypes.c_char, ['a string', 'another string'])
我收到类型错误,这是有道理的:
TypeError: one character bytes, bytearray or integer expected
这可以(有点)通过将字符串拆分为字节(这也有意义)来工作:
from multiprocessing.sharedctypes import Array
multiproccessing.sharedctypes.Array(ctypes.c_char, [b's', b't', b'r', b'i', b'n', b'g'])
但这对于存储大型字符串列表不是很方便。
然而,当我尝试使用文档 here 中显示的 value
和 raw
属性并在 note 中提到时,仍然存在没有魔法:
Array(ctypes.c_char.value, ['string'])
出现此错误:
TypeError: unsupported operand type(s) for *: 'getset_descriptor' and 'int'
和 raw
给出了这个:
Array(ctypes.c_char.raw, ['string'])
AttributeError: type object 'c_char' has no attribute 'raw'
我也试过使用 c_wchar_p
类型,它在原始 C 兼容数据类型的 table 中(在 docs 中找到)直接对应于一个字符串:
Array(ctypes.c_wchar_p, ['string'])
这个CRASHESpython,没有报告错误代码,进程简单地以代码0退出。
为什么 sharedctypes 数组不能像 c_wchar_p 类型那样保存指针?非常欢迎任何其他关于如何在 sharedctype 数组中存储字符串的解决方案或建议!
Update - 此代码 偶尔 有效(大部分时间 python 停止工作但偶尔我会恢复字符串,尽管它们大多是胡言乱语)。但评论提到它在 windows.
上工作正常
from multiprocessing import Process, Lock
from multiprocessing.sharedctypes import Value, Array
import ctypes
def print_strings(S):
"""Print strings in the C array"""
print([a for a in S])
if __name__ == '__main__':
lock = Lock()
string_array = Array(ctypes.c_wchar_p, ['string'])
q = Process(target=print_strings, args=(string_array,))
q.start()
q.join()
更新 2
这是我得到的乱码:
['汣猎癞汥数\ u2e73ਊ††敓\ u2065 \ u200A \ u200A†ⴠⴭⴭⴭ††††\ u2e79 \ u2e79 \ u2e6c \ u2e6c \ u2e6c癞捥慩\u2e6c癞\u0a65\u200a†传琭獥\u200a†ⴠⴭⴭ\u200a†颂\u2065猎\u2065桴\u2065潡潧楲桴\u206d异水獝杽楝楝楢楢楢楢U2064祢\ U200A†䄠牢䄠牢\䄠牢\ \䄠牢\ u2064 \ u2064瑓来瑓来瑓来\ \ u2c5f \u2c5f映牯眠玱莹\ u2064湩潴湩潴敨琠潷椠瑮庆獬嬠ⰰ崸愠崸愠摮ⰸ湩⥦⥦\ u202c \ u2064 \ u2064 \ u0a76 \ u0a76 U2073 \ u2065浥汰祯\ u206e慥档\ u2e6c \ u2e6c删汥\ u2065 \ u2065 \ u2072 \ u200a \ u200a†u200a†u206e莹莹捩椠\u2073润散敭瑮摭崳\u205f獡栠痴温和\u2067\u0a61††数欢漠\u2066⸵攸ㄭ楷桴愠\u206e浲⥱ㄠㄠㄠㄠㄠ \u206e‽〳〰⤰\ u200A†††ⴭⴭⴭⴭⴭ\ \ u200A†⸠\ u202e \u2eㅛ\ u2e43 \ u2e43 \ u202e \ u202e \ u202c \ u202c䌢敨u2072慭桴浥瑡捩湵琐੮ ੮††††敦⨠⨠汤污惯惯\ u206c慌潢慌潢慌潢慌潢䴠瑡敨惯惯\ u206c慔汢\u206c慔汢U202E \ u200A††††信:U2072愠敪\ u2073 \ u2073瑓瑡潩敮瑓瑡潩敮瑓瑡潩敮瑓瑡潩敮祲\ \ \ u200A \ u200A†\u202e2e2e2e2e2e2e2e㉛⁝\ u200 \ u2e 4 \ u2e4d \ u2e4d䄠牢=86=]䄠\u202e瑓来湖\u202c䠺扤浔\u206b景传瑠楴楴惯楬੬††††鹵湖潞潞拝Ⱚㄠ琰\u2068ℰ湩掩2楴2楴楬†††岁›漱敶\ \ \u2e70㌠†††††信: U2e6e牯⽧\ u2d68 \ u2d68暋桰暋桰暋桰暋桰暋桰暋桰暋桰獥獥䴯瑡瑡⽨⽨暋桰暋桰暋桰獥\ u200a \ u200A†䔠慸ੳੳੳੳੳ \u2e70ど䬨⸰⥝\u200a†愠牡祡ㄨ〮\u0a29††㸾‾灮椮⠰せⰮㄠ\u202e\u202b樲⥝\u200a†愠牲祡䬨ㄠ〮‰‰6〰02 ††††⸰㠱㠷㌵㌷カ㘮㘴㘱㐹樴⥝ਊ††', 'ਊ††敓\u2065汁潳\u200a†ⴠⴭ i2e79灳捥\ u2e6c \ u2e6c \ u2e6c \ u202c捳滟\ u2e79灳捥\u2e6c癞\ u2e6c癞\u0a65\u200a†丠琅獥\ u200a†u200a†\ u200a†\ \ u200a†u200a†u200a†u206565 \ u2065 \ u2065 \ u2065 \ u2065 \ u2065 \ u2065 \ u2065 \ u2065猎\ u \U2065猎U206d \ u2064祢䌠\ u2064祢䌠祢䌠敬狝慨⁷ㅛ湥散湥散湥散\ u2064祢\ u200a†䄠牢牯眠\ \ \ u206e润慭ੳ††慰玱莹\ \ u2064湩潴湩潴湩潴湩潴敨敨琠潷椠瑮潷椠瑮牥牥庆庆獬嬠獬嬠獬嬠ⰰⰰⰰ U2064桃扥\ \ u0a76††溃祬潮业\ \ u2073 \ u2065 \ u2065 \ u206e \ u206e慥档\ u2e6c \u2e6c删汥\ u2065 \ u2065 \u200a†琠敭搠浠桥\u206eせ㌬崰楳杮杮䕠䕠⁅牡浠捩椠\u2073润畣敭瑮摠崳\u205f獡栀痴湢敠敀\u2067\u0a61 \u2066⸵\ \ u206e \ u2073景ㄠ\ u2073⠠\ \ u206e \ u200a \ u200a†删晥牥†††††††\ u200a†u200a†⸠\ \ \ \ \ \ \ u202e \ u202eㅛ \ u202e \ u202c䌢敨\ u2073 \ u2073 \ u2072慭桴浥瑡\ u2072慭桴浥瑡慭桴浥瑡慭桴浥瑡污映污映湵琐潩狝੮ ੮ ੮†††敦U206C \ u202eⰵ\ u202eⰵ㩮\ u200A \ u200a†††††信:U202E \ U2072愠敪ㄠ㘹⸲\u200a†⸠\u202e㉛⁝\u2e4d䄠䉢浠睯莹⁺湡\u2064\u2e49䄠\u202e瑓来湖\u202c䠪湡扤浔\u206b景仰慠楬†U2068牰湩\ u202c敎敎敎⁷››漱敶沩岁漱敶ㄠ㘹潆潆\ u2e70㌠ਮ††††敦挮⽡捾瀯条彦੭ ੭††⸮⸮⸮瑴㩰⼯潫挮慰挮慰挮慰\ u2e6e牯⽧\牯⽧牯⽧牯⽧牯⽧瑨䴯瑡䴯瑡䴯瑡\ u2d68暋桰栮浴\ u200A†䔠慸ੳ††ⴭⴭⴭⴭ\ u200A†㸠㸾渠\ u2e70ど嬨\ u200A \ u200a†愠牲\ u0a29†††††㸾㸾㸾㸾〜〜灮椮]樲⥝\u200a†愠牲祡谦ㄠ〮〰〰〰⬰⸰\u206a†††Ⱐ†⸰㠱㠷㌵㌷㘮㘴㘱㐹樴⥝ਊ††']
(是的,显然全部来自 'string',别问我怎么来的)
the documentation中提到了您遇到的问题:
Note: Although it is possible to store a pointer in shared memory remember that this will refer to a location in the address space of a specific process. However, the pointer is quite likely to be invalid in the context of a second process and trying to dereference the pointer from the second process may cause a crash.
这意味着存储指针(如字符串)将不起作用,因为只有地址会到达子进程,而该地址将不再有效(因此出现分段错误)。例如,考虑这种替代方案,其中所有字符串都连接到一个数组中,并且也传递了另一个具有长度的数组(您可以根据自己的方便对其进行调整):
from multiprocessing import Process, Lock
from multiprocessing.sharedctypes import Value, Array
import ctypes
def print_strings(S, S_len):
"""Print strings in the C array"""
received_strings = []
start = 0
for length in S_len:
received_strings.append(S[start:start + length])
start += length
print("received strings:", received_strings)
if __name__ == '__main__':
lock = Lock()
my_strings = ['string1', 'str2']
my_strings_len = [len(s) for s in my_strings]
string_array = Array(ctypes.c_wchar, ''.join(my_strings))
string_len_array = Array(ctypes.c_uint, my_strings_len)
q = Process(target=print_strings, args=(string_array, string_len_array))
q.start()
q.join()
输出:
received strings: ['string1', 'str2']
关于子进程中的地址:
这个问题有点跑题了,但是要发表评论太久了。老实说,这开始超出了我的理解范围,看看 eryksun's comments below for more informed insights, but here's my understanding anyway. On Unix(-like) a new process created through fork
has the same memory and (virtual) addresses than the parent process, but if you then exec
some program that's not the case anymore; I don't know if Python's multiprocessing
runs an exec
or not on Unix (note: see for more on this and set_start_method
), but in any case I wouldn't assume there is any guarantee that any address in the Python-managed memory pool should stay the same. On Windows, CreateProcess
从一个原则上与父进程没有任何共同点的可执行文件创建一个新进程。我认为即使是多个进程使用的共享库 (.so/.dll) 也不应该在任一平台上位于同一地址。我认为在使用共享内存时进程之间共享(虚拟)地址甚至没有意义,因为如果我没记错的话(我可能没有记错),共享内存块被映射到每个进程上的任意虚拟地址。所以我的印象是没有充分的理由(或 "good and obvious",至少)与子进程共享地址(当然,ctypes
中的指针类型对于与同一子进程中的本地库通信仍然有用过程)。
正如我所说,我对此不是 100% 有信心,但我认为总体思路是这样的。
使 .raw
和 .value
正常工作的其他示例。根据文档,它仅适用于 Array(ctypes.c_char,...)
:
from multiprocessing import Process
from multiprocessing.sharedctypes import Value, Array
import ctypes
def print_strings(s):
"""Print strings in the C array"""
print(s.value)
print(len(s))
s[len(s)-1]=b'x'
if __name__ == '__main__':
string_array = Array(ctypes.c_char, b'string')
q = Process(target=print_strings, args=(string_array,))
q.start()
q.join()
print(string_array.raw)
显示共享缓冲区已修改的输出:
b'string'
6
b'strinx'
我正在尝试使用多处理模块的 sharedctypes 部分在进程之间共享一些字符串。
TL;DR: 我希望将我的字符串放入一个 sharedctypes 数组中,如下所示:
from multiprocessing.sharedctypes import Array
Array(ctypes.c_char, ['a string', 'another string'])
更多信息:
docs有这样的注释:
"请注意,ctypes.c_char 的数组具有值和原始属性,允许使用它来存储和检索字符串。"
单独使用c_char
:
from multiprocessing.sharedctypes import Array
Array(ctypes.c_char, ['a string', 'another string'])
我收到类型错误,这是有道理的:
TypeError: one character bytes, bytearray or integer expected
这可以(有点)通过将字符串拆分为字节(这也有意义)来工作:
from multiprocessing.sharedctypes import Array
multiproccessing.sharedctypes.Array(ctypes.c_char, [b's', b't', b'r', b'i', b'n', b'g'])
但这对于存储大型字符串列表不是很方便。
然而,当我尝试使用文档 here 中显示的 value
和 raw
属性并在 note 中提到时,仍然存在没有魔法:
Array(ctypes.c_char.value, ['string'])
出现此错误:
TypeError: unsupported operand type(s) for *: 'getset_descriptor' and 'int'
和 raw
给出了这个:
Array(ctypes.c_char.raw, ['string'])
AttributeError: type object 'c_char' has no attribute 'raw'
我也试过使用 c_wchar_p
类型,它在原始 C 兼容数据类型的 table 中(在 docs 中找到)直接对应于一个字符串:
Array(ctypes.c_wchar_p, ['string'])
这个CRASHESpython,没有报告错误代码,进程简单地以代码0退出。
为什么 sharedctypes 数组不能像 c_wchar_p 类型那样保存指针?非常欢迎任何其他关于如何在 sharedctype 数组中存储字符串的解决方案或建议!
Update - 此代码 偶尔 有效(大部分时间 python 停止工作但偶尔我会恢复字符串,尽管它们大多是胡言乱语)。但评论提到它在 windows.
上工作正常from multiprocessing import Process, Lock
from multiprocessing.sharedctypes import Value, Array
import ctypes
def print_strings(S):
"""Print strings in the C array"""
print([a for a in S])
if __name__ == '__main__':
lock = Lock()
string_array = Array(ctypes.c_wchar_p, ['string'])
q = Process(target=print_strings, args=(string_array,))
q.start()
q.join()
更新 2
这是我得到的乱码:
['汣猎癞汥数\ u2e73ਊ††敓\ u2065 \ u200A \ u200A†ⴠⴭⴭⴭ††††\ u2e79 \ u2e79 \ u2e6c \ u2e6c \ u2e6c癞捥慩\u2e6c癞\u0a65\u200a†传琭獥\u200a†ⴠⴭⴭ\u200a†颂\u2065猎\u2065桴\u2065潡潧楲桴\u206d异水獝杽楝楝楢楢楢楢U2064祢\ U200A†䄠牢䄠牢\䄠牢\ \䄠牢\ u2064 \ u2064瑓来瑓来瑓来\ \ u2c5f \u2c5f映牯眠玱莹\ u2064湩潴湩潴敨琠潷椠瑮庆獬嬠ⰰ崸愠崸愠摮ⰸ湩⥦⥦\ u202c \ u2064 \ u2064 \ u0a76 \ u0a76 U2073 \ u2065浥汰祯\ u206e慥档\ u2e6c \ u2e6c删汥\ u2065 \ u2065 \ u2072 \ u200a \ u200a†u200a†u206e莹莹捩椠\u2073润散敭瑮摭崳\u205f獡栠痴温和\u2067\u0a61††数欢漠\u2066⸵攸ㄭ楷桴愠\u206e浲⥱ㄠㄠㄠㄠㄠ \u206e‽〳〰⤰\ u200A†††ⴭⴭⴭⴭⴭ\ \ u200A†⸠\ u202e \u2eㅛ\ u2e43 \ u2e43 \ u202e \ u202e \ u202c \ u202c䌢敨u2072慭桴浥瑡捩湵琐੮ ੮††††敦⨠⨠汤污惯惯\ u206c慌潢慌潢慌潢慌潢䴠瑡敨惯惯\ u206c慔汢\u206c慔汢U202E \ u200A††††信:U2072愠敪\ u2073 \ u2073瑓瑡潩敮瑓瑡潩敮瑓瑡潩敮瑓瑡潩敮祲\ \ \ u200A \ u200A†\u202e2e2e2e2e2e2e2e㉛⁝\ u200 \ u2e 4 \ u2e4d \ u2e4d䄠牢=86=]䄠\u202e瑓来湖\u202c䠺扤浔\u206b景传瑠楴楴惯楬੬††††鹵湖潞潞拝Ⱚㄠ琰\u2068ℰ湩掩2楴2楴楬†††岁›漱敶\ \ \u2e70㌠†††††信: U2e6e牯⽧\ u2d68 \ u2d68暋桰暋桰暋桰暋桰暋桰暋桰暋桰獥獥䴯瑡瑡⽨⽨暋桰暋桰暋桰獥\ u200a \ u200A†䔠慸ੳੳੳੳੳ \u2e70ど䬨⸰⥝\u200a†愠牡祡ㄨ〮\u0a29††㸾‾灮椮⠰せⰮㄠ\u202e\u202b樲⥝\u200a†愠牲祡䬨ㄠ〮‰‰6〰02 ††††⸰㠱㠷㌵㌷カ㘮㘴㘱㐹樴⥝ਊ††', 'ਊ††敓\u2065汁潳\u200a†ⴠⴭ i2e79灳捥\ u2e6c \ u2e6c \ u2e6c \ u202c捳滟\ u2e79灳捥\u2e6c癞\ u2e6c癞\u0a65\u200a†丠琅獥\ u200a†u200a†\ u200a†\ \ u200a†u200a†u200a†u206565 \ u2065 \ u2065 \ u2065 \ u2065 \ u2065 \ u2065 \ u2065 \ u2065猎\ u \U2065猎U206d \ u2064祢䌠\ u2064祢䌠祢䌠敬狝慨⁷ㅛ湥散湥散湥散\ u2064祢\ u200a†䄠牢牯眠\ \ \ u206e润慭ੳ††慰玱莹\ \ u2064湩潴湩潴湩潴湩潴敨敨琠潷椠瑮潷椠瑮牥牥庆庆獬嬠獬嬠獬嬠ⰰⰰⰰ U2064桃扥\ \ u0a76††溃祬潮业\ \ u2073 \ u2065 \ u2065 \ u206e \ u206e慥档\ u2e6c \u2e6c删汥\ u2065 \ u2065 \u200a†琠敭搠浠桥\u206eせ㌬崰楳杮杮䕠䕠⁅牡浠捩椠\u2073润畣敭瑮摠崳\u205f獡栀痴湢敠敀\u2067\u0a61 \u2066⸵\ \ u206e \ u2073景ㄠ\ u2073⠠\ \ u206e \ u200a \ u200a†删晥牥†††††††\ u200a†u200a†⸠\ \ \ \ \ \ \ u202e \ u202eㅛ \ u202e \ u202c䌢敨\ u2073 \ u2073 \ u2072慭桴浥瑡\ u2072慭桴浥瑡慭桴浥瑡慭桴浥瑡污映污映湵琐潩狝੮ ੮ ੮†††敦U206C \ u202eⰵ\ u202eⰵ㩮\ u200A \ u200a†††††信:U202E \ U2072愠敪ㄠ㘹⸲\u200a†⸠\u202e㉛⁝\u2e4d䄠䉢浠睯莹⁺湡\u2064\u2e49䄠\u202e瑓来湖\u202c䠪湡扤浔\u206b景仰慠楬†U2068牰湩\ u202c敎敎敎⁷››漱敶沩岁漱敶ㄠ㘹潆潆\ u2e70㌠ਮ††††敦挮⽡捾瀯条彦੭ ੭††⸮⸮⸮瑴㩰⼯潫挮慰挮慰挮慰\ u2e6e牯⽧\牯⽧牯⽧牯⽧牯⽧瑨䴯瑡䴯瑡䴯瑡\ u2d68暋桰栮浴\ u200A†䔠慸ੳ††ⴭⴭⴭⴭ\ u200A†㸠㸾渠\ u2e70ど嬨\ u200A \ u200a†愠牲\ u0a29†††††㸾㸾㸾㸾〜〜灮椮]樲⥝\u200a†愠牲祡谦ㄠ〮〰〰〰⬰⸰\u206a†††Ⱐ†⸰㠱㠷㌵㌷㘮㘴㘱㐹樴⥝ਊ††']
(是的,显然全部来自 'string',别问我怎么来的)
the documentation中提到了您遇到的问题:
Note: Although it is possible to store a pointer in shared memory remember that this will refer to a location in the address space of a specific process. However, the pointer is quite likely to be invalid in the context of a second process and trying to dereference the pointer from the second process may cause a crash.
这意味着存储指针(如字符串)将不起作用,因为只有地址会到达子进程,而该地址将不再有效(因此出现分段错误)。例如,考虑这种替代方案,其中所有字符串都连接到一个数组中,并且也传递了另一个具有长度的数组(您可以根据自己的方便对其进行调整):
from multiprocessing import Process, Lock
from multiprocessing.sharedctypes import Value, Array
import ctypes
def print_strings(S, S_len):
"""Print strings in the C array"""
received_strings = []
start = 0
for length in S_len:
received_strings.append(S[start:start + length])
start += length
print("received strings:", received_strings)
if __name__ == '__main__':
lock = Lock()
my_strings = ['string1', 'str2']
my_strings_len = [len(s) for s in my_strings]
string_array = Array(ctypes.c_wchar, ''.join(my_strings))
string_len_array = Array(ctypes.c_uint, my_strings_len)
q = Process(target=print_strings, args=(string_array, string_len_array))
q.start()
q.join()
输出:
received strings: ['string1', 'str2']
关于子进程中的地址:
这个问题有点跑题了,但是要发表评论太久了。老实说,这开始超出了我的理解范围,看看 eryksun's comments below for more informed insights, but here's my understanding anyway. On Unix(-like) a new process created through fork
has the same memory and (virtual) addresses than the parent process, but if you then exec
some program that's not the case anymore; I don't know if Python's multiprocessing
runs an exec
or not on Unix (note: see set_start_method
), but in any case I wouldn't assume there is any guarantee that any address in the Python-managed memory pool should stay the same. On Windows, CreateProcess
从一个原则上与父进程没有任何共同点的可执行文件创建一个新进程。我认为即使是多个进程使用的共享库 (.so/.dll) 也不应该在任一平台上位于同一地址。我认为在使用共享内存时进程之间共享(虚拟)地址甚至没有意义,因为如果我没记错的话(我可能没有记错),共享内存块被映射到每个进程上的任意虚拟地址。所以我的印象是没有充分的理由(或 "good and obvious",至少)与子进程共享地址(当然,ctypes
中的指针类型对于与同一子进程中的本地库通信仍然有用过程)。
正如我所说,我对此不是 100% 有信心,但我认为总体思路是这样的。
使 .raw
和 .value
正常工作的其他示例。根据文档,它仅适用于 Array(ctypes.c_char,...)
:
from multiprocessing import Process
from multiprocessing.sharedctypes import Value, Array
import ctypes
def print_strings(s):
"""Print strings in the C array"""
print(s.value)
print(len(s))
s[len(s)-1]=b'x'
if __name__ == '__main__':
string_array = Array(ctypes.c_char, b'string')
q = Process(target=print_strings, args=(string_array,))
q.start()
q.join()
print(string_array.raw)
显示共享缓冲区已修改的输出:
b'string'
6
b'strinx'