-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy paththreadtools.py
More file actions
105 lines (82 loc) · 2.99 KB
/
threadtools.py
File metadata and controls
105 lines (82 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# -*- coding: utf-8 -*-
"""
Created on Tue Mar 26 11:16:22 2019
@author: WellenWoo
"""
import wx
import threading
from types import FunctionType, MethodType
__all__ = ["callafter", "synchfunc", "ClassSynchronizer"]
def callafter(func):
"""Decorator to automatically use CallAfter if
a method is called from a different thread.
"""
def callafterwrap(*args, **kw):
# if wx.Thread_IsMain(): #wx Classic
if wx.IsMainThread(): # wx Phoenix
return func(*args, **kw)
else:
wx.CallAfter(func, *args, **kw)
callafterwrap.__name__ = func.__name__
callafterwrap.__doc__ = func.__doc__
return callafterwrap
def synchfunc(func):
"""Decorator to synchronize a method call from a worker
thread to the GUI thread.
"""
def synchwrap(*args, **kw):
if wx.IsMainThread():
return func(*args, **kw)
else:
synchobj = Synchronizer(func, args, kw)
return synchobj.run()
synchwrap.__name__ = func.__name__
synchwrap.__doc__ = func.__doc__
return synchwrap
class Synchronizer(object):
"""Synchronize CallAfter calls"""
def __init__(self, func, args, kw):
super(Synchronizer, self).__init__()
self.func = func
self.args = args
self.kw = kw
self._synch = threading.Semaphore(0)
def _asynch_wrapper(self):
"""This part runs in main gui thread"""
try:
self.result = self.func(*self.args, **self.kw)
except Exception as msg:
self.exception = msg
# Release Semaphore to allow processing back
# on other thread to resume.
self._synch.release() # 释放线程锁
def run(self):
"""Call from background thread"""
# Make sure this is not called from main thread
# as it will result in deadlock waiting on the
# Semaphore.
assert not wx.IsMainThread(), "DeadLock"
# Make the asynchronous call to the main thread
# to run the function.
wx.CallAfter(self._asynch_wrapper)
# Block on Semaphore release until the function
# has been processed in the main thread by the
# UI's event loop.
self._synch.acquire() # 阻塞
# Return result to caller or raise error
try:
return self.result
except AttributeError:
raise self.exception
class ClassSynchronizer(type):
"""Metaclass to make all methods in a class threadsafe"""
def __call__(mcs, *args, **kw):
obj = type.__call__(mcs, *args, **kw)
# Wrap all methods/functions in the class with
# the synchfunct decorator.
for attrname in dir(obj):
attr = getattr(obj, attrname)
if type(attr) in (MethodType, FunctionType):
nfunc = synchfunc(attr)
setattr(obj, attrname, nfunc)
return obj