Ответ 1
Вы не можете напрямую делать то, что хотите. В фоновом потоке запущена функция run
, которая просто петли навсегда, поэтому она не может ничего сделать.
Вы можете, конечно, вызвать методы класса в своем потоке, но это, вероятно, не то, что вы хотите здесь.
Рамочные схемы, такие как Qt,.NET или Cocoa, могут предложить методы runOnOtherThread
-type, так это то, что каждый поток запускает "цикл событий", поэтому все, что они действительно делают, это размещение события. Вы можете сделать это самостоятельно, если переписать метод run
в цикл событий. Например:
import queue
import threading
class SomeClass(threading.Thread):
def __init__(self, q, loop_time = 1.0/60):
self.q = q
self.timeout = loop_time
super(SomeClass, self).__init__()
def onThread(self, function, *args, **kwargs):
self.q.put((function, args, kwargs))
def run(self):
while True:
try:
function, args, kwargs = self.q.get(timeout=self.timeout)
function(*args, **kwargs)
except queue.Empty:
self.idle()
def idle(self):
# put the code you would have put in the `run` loop here
def doSomething(self):
pass
def doSomethingElse(self):
pass
Теперь вы можете сделать это:
someClass = SomeClass()
someClass.start()
someClass.onThread(someClass.doSomething)
someClass.onThread(someClass.doSomethingElse)
someClass.onThread(someClass.doSomething)
Если вы хотите немного упростить интерфейс вызова, за счет большего количества кода в классе, вы можете добавить методы-обертки следующим образом:
def _doSomething(self):
# put the real code here
def doSomething(self):
self.onThread(self._doSomething)
Однако, если ваш метод idle
не работает, вы на самом деле просто строите эквивалент однопоточного пула потоков здесь, и есть гораздо более простые способы сделать это, чем создавать его с нуля. Например, используя модуль futures
от PyPI (backport модуля Python 3 concurrent.futures
):
import futures
class SomeClass(object):
def doSomething(self):
pass
def doSomethingElse(self):
pass
someClass = SomeClass()
with futures.ThreadPoolExecutor(1) as executor:
executor.submit(someClass.doSomething)
executor.submit(someClass.doSomethingElse)
executor.submit(someClass.doSomething)
Или, только с помощью stdlib:
from multiprocessing import dummy as multithreading
class SomeClass(object):
def doSomething(self):
pass
def doSomethingElse(self):
pass
someClass = SomeClass()
pool = multithreading.Pool(1)
pool.apply(someClass.doSomething)
pool.apply(someClass.doSomethingElse)
pool.apply(someClass.doSomething)
pool.close()
pool.join()
У пулов есть и другие преимущества, а исполнители - еще больше. Например, что, если методы возвращают значения, и вы хотите запустить две функции, затем дождаться результатов, а затем начать третью с результатами первых двух? Легко:
with futures.ThreadPoolExecutor(1) as executor:
f1 = executor.submit(someClass.doSomething)
f2 = executor.submit(someClass.doSomethingElse)
futures.wait((f1, f2))
f3 = executor.submit(someClass.doSomethingElser, f1.result(), f2.result())
result = f3.result()
Даже если вы позже переключитесь на пул из 4 потоков, так что f1
и f2
могут быть ожидающими одновременно, а f2
может даже вернуться первым, вы гарантированно начнете с doSomethingElser
, как только оба из них закончены, и не раньше.
Здесь есть еще одна возможность. Вам действительно нужен код для запуска в этом потоке, или вам просто нужно изменить переменные, от которых зависит поток? Если это последнее, просто синхронизируйте доступ к переменным. Например:
class SomeClass(threading.Thread):
def __init__(self):
self.things_lock = threading.Lock()
self.things = []
while True:
with self.lock:
things = self.things[:]
for thing in things:
# pass
def doSomething(self):
with self.lock:
self.things.append(0)
someClass = SomeClass()
someClass.start()
someClass.doSomething()
Там нет ничего волшебного в том, чтобы быть на главной теме здесь. Если, кроме необходимости изменять переменные, от которых зависит SomeClass
, вы также хотели просто ударить doSomething
с основного потока, чтобы вы могли делать больше важных вещей, чем просто ждать, пока он закончится, вы можете создать короткий -lested extra thread только до doSomething
:
someClass = SomeClass()
someClass.start()
somethingThread = threading.Thread(target=someClass.doSomething)
somethingThread.start()
doOtherImportantStuffWithSomethingIsHappening()
somethingThread.join()