{"action":"opened","issue":{"url":"https://api.github.com/repos/jek/blinker/issues/21","labels_url":"https://api.github.com/repos/jek/blinker/issues/21/labels{/name}","comments_url":"https://api.github.com/repos/jek/blinker/issues/21/comments","events_url":"https://api.github.com/repos/jek/blinker/issues/21/events","html_url":"https://github.com/jek/blinker/issues/21","id":107347519,"number":21,"title":"@_utilities.lazy_property isn't thread safe","user":{"login":"harlowja","id":970458,"avatar_url":"https://avatars.githubusercontent.com/u/970458?v=3","gravatar_id":"","url":"https://api.github.com/users/harlowja","html_url":"https://github.com/harlowja","followers_url":"https://api.github.com/users/harlowja/followers","following_url":"https://api.github.com/users/harlowja/following{/other_user}","gists_url":"https://api.github.com/users/harlowja/gists{/gist_id}","starred_url":"https://api.github.com/users/harlowja/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/harlowja/subscriptions","organizations_url":"https://api.github.com/users/harlowja/orgs","repos_url":"https://api.github.com/users/harlowja/repos","events_url":"https://api.github.com/users/harlowja/events{/privacy}","received_events_url":"https://api.github.com/users/harlowja/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-09-19T17:55:42Z","updated_at":"2015-09-19T17:55:42Z","closed_at":null,"body":"Just thought I'd let u know (from having made this same mistake myself),\r\n\r\nBut the application of ``@_utilities.lazy_property`` isn't actually thread-safe.\r\n\r\nAn example program you can try:\r\n\r\n```\r\nimport threading\r\nimport thread\r\n\r\nimport time\r\n\r\nfrom blinker import _utilities\r\n\r\n\r\nclass SlowThing(object):\r\n\r\n    @_utilities.lazy_property\r\n    def b(self):\r\n        print \"starting reading 'b' thread %s\" % thread.get_ident()\r\n        time.sleep(0.5)\r\n        print \"finished reading 'b' thread %s\" % thread.get_ident()\r\n        return 1\r\n\r\ns = SlowThing()\r\n\r\n\r\ndef _run():\r\n    s.b\r\n\r\n\r\nthreads = []\r\nfor i in range(0, 5):\r\n    t = threading.Thread(target=_run)\r\n    threads.append(t)\r\n    t.start()\r\n\r\n\r\nwhile threads:\r\n    t = threads.pop()\r\n    t.join()\r\n\r\n```\r\n\r\nThe output of this will create:\r\n\r\n```\r\nstarting reading 'b' thread 140684440516352\r\nstarting reading 'b' thread 140684432123648\r\nstarting reading 'b' thread 140684423730944\r\nstarting reading 'b' thread 140684415338240\r\nstarting reading 'b' thread 140684203915008\r\nfinished reading 'b' thread 140684432123648\r\nfinished reading 'b' thread 140684440516352\r\nfinished reading 'b' thread 140684423730944\r\nfinished reading 'b' thread 140684415338240\r\nfinished reading 'b' thread 140684203915008\r\n```\r\n\r\nSo u can see there are many threads at the same time recomputing that property (in this case it doesn't matter, but in other cases it might?)\r\n\r\nSome code that I have made that does appear to work fine:\r\n\r\nPerhaps we can share it somehow...\r\n\r\nhttps://github.com/openstack/taskflow/blob/master/taskflow/utils/misc.py#L346"}}