uuid.uuid1、uuid_generate_time 和线程
在Python中,我发现可以通过在多个线程中反复调用uuid.uuid1()
来产生一个段错误(seg fault)。经过一些调查,这个函数最终通过ctypes
调用了C语言中的uuid_generate_time
函数。
在uuid.py
文件中:
for libname in ['uuid', 'c']:
try:
lib = ctypes.CDLL(ctypes.util.find_library(libname))
except:
continue
if hasattr(lib, 'uuid_generate_random'):
_uuid_generate_random = lib.uuid_generate_random
if hasattr(lib, 'uuid_generate_time'):
_uuid_generate_time = lib.uuid_generate_time
if _uuid_generate_random is not None:
break # found everything we were looking for
然后在uuid1()
的定义中:
def uuid1(node=None, clock_seq=None):
"""Generate a UUID from a host ID, sequence number, and the current time.
If 'node' is not given, getnode() is used to obtain the hardware
address. If 'clock_seq' is given, it is used as the sequence number;
otherwise a random 14-bit sequence number is chosen."""
# When the system provides a version-1 UUID generator, use it (but don't
# use UuidCreate here because its UUIDs don't conform to RFC 4122).
if _uuid_generate_time and node is clock_seq is None:
_buffer = ctypes.create_string_buffer(16)
_uuid_generate_time(_buffer)
return UUID(bytes=_buffer.raw)
我查阅了uuid_generate_time
的手册以及Python文档中的uuid.uuid1
,但没有看到关于线程安全的说明。我猜这可能和它需要访问系统时钟和/或MAC地址有关,但这只是我的一个盲目猜测。
我想知道有没有人能给我一些启发?
以下是我用来产生段错误的代码:
import threading, uuid
# XXX If I use a lock, I can avoid the seg fault
#uuid_lock = threading.Lock()
def test_uuid(test_func):
for i in xrange(100):
test_func()
#with uuid_lock:
# test_func()
def test(test_func, threads):
print 'Running %s with %s threads...' % (test_func.__name__, threads)
workers = [threading.Thread(target=test_uuid, args=(test_func,)) for x in xrange(threads)]
[x.start() for x in workers]
[x.join() for x in workers]
print 'Done!'
if __name__ == '__main__':
test(uuid.uuid4, 8)
test(uuid.uuid1, 8)
我得到的输出是:
Running uuid4 with 8 threads...
Done!
Running uuid1 with 8 threads...
Segmentation Fault (core dumped)
哦,对了,我是在Solaris系统上运行这个的……
1 个回答
0
文档没有说明它是线程安全的,所以你不能假设它是线程安全的。这一点很简单。
查看当前的 OpenIndiana 源代码,对于 uuid_generate_time
函数,导致崩溃的原因并不是很明显。不过,这个函数确实使用了一个锁,但在进行一些初始化任务时并没有保持这个锁。这可能和问题有关,但我无法指出具体哪个地方的竞争条件会导致错误。你可以尝试在启动任何线程之前先调用一次 uuid1
,看看这样是否能解决问题。不过,最好还是自己使用一个锁,因为没有保证 Python 代码本身是线程安全的。