uuid.uuid1、uuid_generate_time和线程

3
在Python中,我可以在uuid模块中一致地生成一个段错误。这可以通过从多个线程重复调用uuid.uuid1()来完成。经过一些挖掘,似乎该函数最终通过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上运行这个程序...


就我所知,我在Python 2.7.8上的OS X 10.9.4上多次运行了这个程序,并且甚至将线程增加到100,但并没有出现段错误。这可能是一个特定于平台的问题。 - Jason S
@JasonS 是的,我就怕出现这种情况,这是在Solaris上;我会在问题中做个记录。谢谢! - beetea
cpython代码中有一条注释,说明它不是线程安全的:https://github.com/python/cpython/blob/master/Lib/uuid.py#L481 - Gonzalo Matheu
我们遇到了类似的问题,但是与段错误不同,我们达到了打开文件限制。 例如:https://github.com/dln/python-libuuid/issues/1 - Gonzalo Matheu
1个回答

0

文档没有说明它是线程安全的,因此您不能假设它是线程安全的。就这么简单。

查看当前OpenIndiana源代码中的uuid_generate_time,并不完全清楚是什么导致了段错误。然而,虽然该函数使用了锁,但在执行一些初始化任务时并没有持有该锁。这可能与问题有关,但我无法指出哪个具体位置会导致竞争条件引起故障。您可以尝试在启动任何线程之前调用uuid1一次,看看是否可以解决问题。但最好使用自己的锁,因为无法保证Python代码本身是线程安全的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接