Python'daki arayan iş parçacığında bir iş parçacığının özel durumunu yakalama


208

Python ve genel olarak çok iş parçacıklı programlama konusunda çok yeniyim. Temelde, dosyaları başka bir konuma kopyalayacak bir komut dosyası var. ....Komut dosyası hala çalıştığını belirtmek için çıktı böylece bu başka bir iş parçacığına yerleştirilmesini istiyorum .

Sahip olduğum sorun dosyaları kopyalanamazsa bir istisna atmak olmasıdır. Ana iş parçacığında çalışan bu Tamam; ancak, aşağıdaki koda sahip olmak işe yaramaz:

try:
    threadClass = TheThread(param1, param2, etc.)
    threadClass.start()   ##### **Exception takes place here**
except:
    print "Caught an exception"

İş parçacığı sınıfının kendisinde, istisnayı yeniden atmaya çalıştım, ama çalışmıyor. Buradaki insanların benzer sorular sorduğunu gördüm, ancak hepsi yapmaya çalıştığımdan daha spesifik bir şey yapıyor gibi görünüyor (ve sunulan çözümleri tam olarak anlamıyorum). İnsanların kullanımından bahsettiğini gördüm sys.exc_info(), ancak nerede veya nasıl kullanacağımı bilmiyorum.

Tüm yardımlar büyük beğeni topluyor!

EDIT: thread sınıfının kodu aşağıdadır:

class TheThread(threading.Thread):
    def __init__(self, sourceFolder, destFolder):
        threading.Thread.__init__(self)
        self.sourceFolder = sourceFolder
        self.destFolder = destFolder

    def run(self):
        try:
           shul.copytree(self.sourceFolder, self.destFolder)
        except:
           raise

İçinde neler olduğu hakkında daha fazla bilgi verebilir misiniz TheThread? Kod örneği belki?
jonathanizm

Elbette. Yukarıdaki ayrıntıları bazı ayrıntıları içerecek şekilde düzenleyeceğim.
Phanto

1
Ana İş Parçasını bir şeyler yapan bit ve ilerleme göstergesi ortaya çıkan İş Parçacığında olacak şekilde değiştirmeyi düşündünüz mü?
Dan Head

1
Dan Head, önce "..." fonksiyonunu üreten ve daha sonra kopyalama fonksiyonunu çalıştıran ana iş parçacığından mı bahsediyorsunuz? Bu işe yarayabilir ve istisna sorununu önleyebilir. Ama yine de düzgün bir şekilde python iplik nasıl öğrenmek istiyorum.
Phanto

Yanıtlar:


114

Sorun thread_obj.start()hemen geri dönüyor. Yumurtladığınız alt iş parçacığı kendi bağlamında, kendi yığını ile yürütülür. Orada meydana gelen herhangi bir istisna alt iş parçacığı bağlamında ve kendi yığınında. Bu bilgiyi ana iş parçacığına iletmek için şu anda düşünebileceğim bir yol, iletiyi geçen bir tür kullanmaktır, bu yüzden buna bakabilirsiniz.

Boyut için bunu deneyin:

import sys
import threading
import Queue


class ExcThread(threading.Thread):

    def __init__(self, bucket):
        threading.Thread.__init__(self)
        self.bucket = bucket

    def run(self):
        try:
            raise Exception('An error occured here.')
        except Exception:
            self.bucket.put(sys.exc_info())


def main():
    bucket = Queue.Queue()
    thread_obj = ExcThread(bucket)
    thread_obj.start()

    while True:
        try:
            exc = bucket.get(block=False)
        except Queue.Empty:
            pass
        else:
            exc_type, exc_obj, exc_trace = exc
            # deal with the exception
            print exc_type, exc_obj
            print exc_trace

        thread_obj.join(0.1)
        if thread_obj.isAlive():
            continue
        else:
            break


if __name__ == '__main__':
    main()

5
Neden bu çirkin döngü yerine iş parçacığına katılmıyorsunuz? multiprocessingEşdeğerini görün : gist.github.com/2311116
schlamar

1
@Lasse yanıtına dayalı olarak EventHook kalıbını stackoverflow.com/questions/1092531/event-system-in-python/… kullanmıyorsunuz? Döngü olayından ziyade?
Andre Miras

1
Kuyruk, tam bir kuyruğa sahip olmak istemiyorsanız, bir hatayı iletmek için en iyi araç değildir. Çok daha iyi bir yapı diş
açıyor.Event

1
Bu bana güvensiz görünüyor. İplik sonra bir istisna hakkını yükseltir ne olur bucket.get()zamlar Queue.Empty? Sonra iş parçacığı join(0.1)tamamlanacak ve isAlive() is Falseistisnanızı özleyeceksiniz.
Steve

1
Queuebu basit durumda gereksizdir - istisnanın hemen ardından (bu basit örnekte olduğu gibi) tamamlandığından ExcThreademin olduğunuz sürece istisna bilgilerini bir özelliği olarak saklayabilirsiniz run(). Ardından, istisnayı sonrasında (veya sırasında) yeniden yükseltirsiniz t.join(). İş join()parçacığının tamamlandığından emin olduğundan senkronizasyon sorunu yoktur . Aşağıdaki yanıtlara bakınız: Rok Strniša stackoverflow.com/a/12223550/126362
ejm

42

concurrent.futuresModül basit ayrı ipler (veya işlemler) işi yapmak ve ortaya çıkan herhangi istisnalar işlemek için yapar:

import concurrent.futures
import shutil

def copytree_with_dots(src_path, dst_path):
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
        # Execute the copy on a separate thread,
        # creating a future object to track progress.
        future = executor.submit(shutil.copytree, src_path, dst_path)

        while future.running():
            # Print pretty dots here.
            pass

        # Return the value returned by shutil.copytree(), None.
        # Raise any exceptions raised during the copy process.
        return future.result()

concurrent.futuresPython 3.2 ile birlikte gelir ve önceki sürümler için backported futuresmodülü olarak bulunur .


5
Bu OP'nin istediğini tam olarak yapmasa da, tam olarak ihtiyacım olan ipucu. Teşekkür ederim.
Mad Physicist

2
Ve ile concurrent.futures.as_completedistisnalar yetiştirilir olarak, hemen bildirilir alabilirsiniz: stackoverflow.com/questions/2829329/...
Ciro Santilli郝海东冠状病六四事件法轮功

1
Bu kod ana iş parçacığını engelliyor. Bunu eşzamansız olarak nasıl yaparsınız?
Nikolay Shindarov

40

Bu soruya gerçekten garip bir şekilde karmaşık cevaplar var. Bunu basitleştiriyor muyum, çünkü bu benim için çoğu şey için yeterli görünüyor.

from threading import Thread

class PropagatingThread(Thread):
    def run(self):
        self.exc = None
        try:
            if hasattr(self, '_Thread__target'):
                # Thread uses name mangling prior to Python 3.
                self.ret = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            else:
                self.ret = self._target(*self._args, **self._kwargs)
        except BaseException as e:
            self.exc = e

    def join(self):
        super(PropagatingThread, self).join()
        if self.exc:
            raise self.exc
        return self.ret

Python'un yalnızca bir veya başka bir sürümünde çalışacağınızdan eminseniz, run()yöntemi yalnızca yönetilen sürüme indirgeyebilirsiniz (3'ten önce yalnızca Python sürümlerinde çalışacaksanız) veya yalnızca temiz sürüm (yalnızca 3 ile başlayan Python sürümlerinde çalışacaksanız).

Örnek kullanım:

def f(*args, **kwargs):
    print(args)
    print(kwargs)
    raise Exception('I suck at this')

t = PropagatingThread(target=f, args=(5,), kwargs={'hello':'world'})
t.start()
t.join()

Ve katıldığınızda diğer iş parçacığındaki özel durumun arttığını göreceksiniz.

sixYalnızca Python 3 kullanıyorsanız veya kullanıyorsanız , kural dışı durum yeniden oluşturulduğunda aldığınız yığın izleme bilgilerini geliştirebilirsiniz. Yalnızca birleşim noktasındaki yığın yerine, iç istisnayı yeni bir dış istisnaya sarabilir ve her iki yığın izlemesini

six.raise_from(RuntimeError('Exception in thread'),self.exc)

veya

raise RuntimeError('Exception in thread') from self.exc

1
Bu cevabın neden daha popüler olmadığından emin değilim. Burada basit yayılım yapan, ancak bir sınıfı genişletmeyi ve geçersiz kılmayı gerektiren başkaları da var. Bu sadece birçoğunun beklediği şeyi yapar ve sadece İplikten ProagatingThread'e değiştirmeyi gerektirir. Ve 4 boşluk sekmesi böylece benim kopyala / yapıştır önemsiz :-) ... önerdiğim tek gelişme six.raise_from () kullanmaktır, böylece sadece yığın için yığın yerine güzel bir iç içe yığın izleri alırsınız. sitenin yeniden düzenlenmesi.
aggieNick02

Çok teşekkür ederim. Çok basit bir çözüm.
sonulohani

Benim sorunum, birden fazla alt iş parçacığı var olmasıdır. Birleştirmeler sırayla yürütülür ve istisna sonraki birleştirilen iş parçacıklarından yükseltilebilir. Sorunuma basit bir çözüm var mı? aynı anda katılmak?
chuan

Teşekkürler, mükemmel çalışıyor! Neden doğrudan piton tarafından ele alındığından emin değilim…
GG.

Bu en yararlı cevap definetelu, bu sulution diğerlerinden çok daha genel ama basit. Bir projede kullanacak!
Konstantin Sekeresh

30

Farklı bir iş parçacığında atılan bir istisnayı doğrudan yakalamak mümkün olmasa da, işte bu işleve çok yakın bir şey elde etmek için bir kod. Alt iş parçacığınızın ExThreadyerine sınıfın alt sınıfı threading.Threadolmalıdır ve iş parçacığının işini bitirmesini beklerken üst iş parçacığı child_thread.join_with_exception()yöntemi yerine çağırmalıdır child_thread.join().

Bu uygulamanın teknik ayrıntıları: alt iş parçacığı bir istisna attığında, üst öğeye a aracılığıyla iletilir Queueve üst iş parçacığına yeniden atılır. Bu yaklaşımda meşgul bir beklemenin olmadığına dikkat edin.

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except BaseException:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    def run_with_exception(self):
        thread_name = threading.current_thread().name
        raise MyException("An error in thread '{}'.".format(thread_name))

def main():
    t = MyThread()
    t.start()
    try:
        t.join_with_exception()
    except MyException as ex:
        thread_name = threading.current_thread().name
        print "Caught a MyException in thread '{}': {}".format(thread_name, ex)

if __name__ == '__main__':
    main()

1
Yakalamak BaseExceptionistemez Exceptionmisin? Tek yaptığınız istisnayı birinden Threaddiğerine yaymak . Şu anda, IE, a KeyboardInterrupt, bir arka plan iş parçacığında kaldırılırsa sessizce yok sayılır.
ArtOfWarfare

join_with_exceptionölü bir iplikte ikinci kez çağrılırsa süresiz olarak asılı kalır. Düzeltme: github.com/fraserharris/threading-extensions/blob/master/…
Fraser Harris

Bence Queuegerekli değil ; @ Noel Baba'nın cevabı hakkındaki yorumuma bakın. Bunu Rok Strniša'nın aşağıdaki cevabı gibi bir şeye basitleştirebilirsiniz stackoverflow.com/a/12223550/126362
ejm

22

Bir iş parçacığında bir istisna oluşursa, en iyi yol sırasında sırasında iş parçacığı iş parçacığında yeniden yükseltmektir join. Şu anda işlenen istisna hakkında sys.exc_info()işlevi kullanarak bilgi alabilirsiniz . Bu bilgi, çağrılıncaya kadar iş parçacığı nesnesinin bir özelliği olarak depolanabilir; joinbu noktada yeniden yükseltilebilir.

Bir o Not Queue.Queue(diğer yanıtlar önerilen) iplik bu basit durumda gerekli değildir en fazla 1 istisna atar ve sağ istisna atma sonra tamamlanana . İpliğin tamamlanmasını bekleyerek yarış koşullarından kaçınırız.

Örneğin, uzatın ExcThread(aşağıda), geçersiz kılın excRun(yerine run).

Python 2.x:

import threading

class ExcThread(threading.Thread):
  def excRun(self):
    pass

  def run(self):
    self.exc = None
    try:
      # Possibly throws an exception
      self.excRun()
    except:
      import sys
      self.exc = sys.exc_info()
      # Save details of the exception thrown but don't rethrow,
      # just complete the function

  def join(self):
    threading.Thread.join(self)
    if self.exc:
      msg = "Thread '%s' threw an exception: %s" % (self.getName(), self.exc[1])
      new_exc = Exception(msg)
      raise new_exc.__class__, new_exc, self.exc[2]

Python 3.x:

İçin 3 bağımsız değişken formu raisePython 3'te gitti, bu nedenle son satırı şu şekilde değiştirin:

raise new_exc.with_traceback(self.exc[2])

2
Neden super (ExcThread, self) .join () yerine threading.Thread.join (self) kullanıyorsunuz?
Richard Möhn

9

concurrent.futures.as_completed

https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed

Aşağıdaki çözüm:

  • bir istisna çağrıldığında hemen ana iş parçacığına döner
  • gerekmediği için ekstra kullanıcı tanımlı sınıf gerektirmez:
    • açık Queue
    • iş parçanızın çevresine başka bir şey eklemek için

Kaynak:

#!/usr/bin/env python3

import concurrent.futures
import time

def func_that_raises(do_raise):
    for i in range(3):
        print(i)
        time.sleep(0.1)
    if do_raise:
        raise Exception()
    for i in range(3):
        print(i)
        time.sleep(0.1)

with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
    futures = []
    futures.append(executor.submit(func_that_raises, False))
    futures.append(executor.submit(func_that_raises, True))
    for future in concurrent.futures.as_completed(futures):
        print(repr(future.exception()))

Olası çıktı:

0
0
1
1
2
2
0
Exception()
1
2
None

Maalesef biri başarısız olduğu için diğerlerini iptal etmek için vadeli işlemleri öldürmek mümkün değildir:

Gibi bir şey yaparsanız:

for future in concurrent.futures.as_completed(futures):
    if future.exception() is not None:
        raise future.exception()

sonra withyakalar ve devam etmeden önce ikinci iş parçacığının bitmesini bekler. Aşağıdakiler benzer şekilde davranır:

for future in concurrent.futures.as_completed(futures):
    future.result()

çünkü future.result()biri meydana gelirse istisnayı yeniden yükseltir.

Tüm Python işleminden çıkmak istiyorsanız, kurtulabilirsiniz os._exit(0), ancak bu muhtemelen bir refactor'a ihtiyacınız olduğu anlamına gelir.

Mükemmel istisna semantiğine sahip özel sınıf

Şu anda kendim için mükemmel bir arayüz kodladım: Aynı anda çalışan maksimum iş parçacığı sayısını sınırlamanın doğru yolu? "Hata işleme ile kuyruk örneği" bölümü. Bu sınıf hem kolaylık sağlamayı hem de gönderim ve sonuç / hata işleme üzerinde tam kontrol sağlamayı amaçlamaktadır.

Python 3.6.7, Ubuntu 18.04 üzerinde test edildi.


4

Bu kötü bir küçük problemdi ve çözümümü içine atmak istiyorum. Bulduğum diğer bazı çözümler (örneğin async.io) umut verici görünüyordu, ancak biraz kara kutu da sundu. Kuyruk / olay döngüsü yaklaşımı sizi belirli bir uygulamaya bağlar. Bununla birlikte, eşzamanlı vadeli işlem kaynak kodu sadece 1000 satır civarındadır ve anlaşılması kolaydır . Sorunumu kolayca çözmeme izin verdi: fazla kurulum yapmadan geçici işçi iş parçacıkları oluşturma ve ana iş parçacığındaki istisnaları yakalayabilme.

Çözümüm, eş zamanlı vadeli işlemler API'sını ve iş parçacığı API'sını kullanıyor. Size hem iş parçacığını hem de geleceği veren bir işçi yaratmanıza izin verir. Bu şekilde, sonucu beklemek için ileti dizisine katılabilirsiniz:

worker = Worker(test)
thread = worker.start()
thread.join()
print(worker.future.result())

... ya da işçinin işiniz bittiğinde bir geri arama göndermesine izin verebilirsiniz:

worker = Worker(test)
thread = worker.start(lambda x: print('callback', x))

... veya etkinlik tamamlanana kadar döngü yapabilirsiniz:

worker = Worker(test)
thread = worker.start()

while True:
    print("waiting")
    if worker.future.done():
        exc = worker.future.exception()
        print('exception?', exc)
        result = worker.future.result()
        print('result', result)           
        break
    time.sleep(0.25)

İşte kod:

from concurrent.futures import Future
import threading
import time

class Worker(object):
    def __init__(self, fn, args=()):
        self.future = Future()
        self._fn = fn
        self._args = args

    def start(self, cb=None):
        self._cb = cb
        self.future.set_running_or_notify_cancel()
        thread = threading.Thread(target=self.run, args=())
        thread.daemon = True #this will continue thread execution after the main thread runs out of code - you can still ctrl + c or kill the process
        thread.start()
        return thread

    def run(self):
        try:
            self.future.set_result(self._fn(*self._args))
        except BaseException as e:
            self.future.set_exception(e)

        if(self._cb):
            self._cb(self.future.result())

... ve test fonksiyonu:

def test(*args):
    print('args are', args)
    time.sleep(2)
    raise Exception('foo')

2

Diş çekme konusunda bir noobie olarak Mateusz Kobos'un kodunun nasıl uygulanacağını anlamak uzun zaman aldı (yukarıda). İşte nasıl kullanılacağını anlamaya yardımcı olacak açık bir sürüm.

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except Exception:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    # This overrides the "run_with_exception" from class "ExThread"
    # Note, this is where the actual thread to be run lives. The thread
    # to be run could also call a method or be passed in as an object
    def run_with_exception(self):
        # Code will function until the int
        print "sleeping 5 seconds"
        import time
        for i in 1, 2, 3, 4, 5:
            print i
            time.sleep(1) 
        # Thread should break here
        int("str")
# I'm honestly not sure why these appear here? So, I removed them. 
# Perhaps Mateusz can clarify?        
#         thread_name = threading.current_thread().name
#         raise MyException("An error in thread '{}'.".format(thread_name))

if __name__ == '__main__':
    # The code lives in MyThread in this example. So creating the MyThread 
    # object set the code to be run (but does not start it yet)
    t = MyThread()
    # This actually starts the thread
    t.start()
    print
    print ("Notice 't.start()' is considered to have completed, although" 
           " the countdown continues in its new thread. So you code "
           "can tinue into new processing.")
    # Now that the thread is running, the join allows for monitoring of it
    try:
        t.join_with_exception()
    # should be able to be replace "Exception" with specific error (untested)
    except Exception, e: 
        print
        print "Exceptioon was caught and control passed back to the main thread"
        print "Do some handling here...or raise a custom exception "
        thread_name = threading.current_thread().name
        e = ("Caught a MyException in thread: '" + 
             str(thread_name) + 
             "' [" + str(e) + "]")
        raise Exception(e) # Or custom class of exception, such as MyException

2

RickardSjogren'in Queue, sys vb. Değil, sinyallere bazı dinleyicileri olmadan benzer şekilde: doğrudan bir dış bloğa karşılık gelen bir istisna işleyicisi çalıştırın.

#!/usr/bin/env python3

import threading

class ExceptionThread(threading.Thread):

    def __init__(self, callback=None, *args, **kwargs):
        """
        Redirect exceptions of thread to an exception handler.

        :param callback: function to handle occured exception
        :type callback: function(thread, exception)
        :param args: arguments for threading.Thread()
        :type args: tuple
        :param kwargs: keyword arguments for threading.Thread()
        :type kwargs: dict
        """
        self._callback = callback
        super().__init__(*args, **kwargs)

    def run(self):
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        except BaseException as e:
            if self._callback is None:
                raise e
            else:
                self._callback(self, e)
        finally:
            # Avoid a refcycle if the thread is running a function with
            # an argument that has a member that points to the thread.
            del self._target, self._args, self._kwargs, self._callback

Normalde iş parçacığına yalnızca self._callback ve run () öğesindeki hariç blok eklenir.


2

Burada partiye biraz geç kaldığımı biliyorum ama çok benzer bir sorun yaşıyordum ama bir GUI olarak tkinter kullanmayı içeriyordu ve mainloop .join () 'ye bağlı çözümlerden herhangi birini kullanmayı imkansız hale getirdi. Bu nedenle, orijinal sorunun EDIT'inde verilen çözümü uyarladım, ancak başkaları için daha kolay anlaşılmasını sağladım.

İşte yeni iş parçacığı sınıfı:

import threading
import traceback
import logging


class ExceptionThread(threading.Thread):
    def __init__(self, *args, **kwargs):
        threading.Thread.__init__(self, *args, **kwargs)

    def run(self):
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        except Exception:
            logging.error(traceback.format_exc())


def test_function_1(input):
    raise IndexError(input)


if __name__ == "__main__":
    input = 'useful'

    t1 = ExceptionThread(target=test_function_1, args=[input])
    t1.start()

Tabii ki, her zaman istisnayı günlüğe kaydetmekten, yazdırmak veya konsoldan çıktı almak gibi başka bir yolla hallettirebilirsiniz.

Bu, ExceptionThread sınıfını, özel bir değişiklik yapmadan tam olarak Thread sınıfında olduğu gibi kullanmanızı sağlar.


1

Sevdiğim yöntemlerden biri gözlemci modeline dayanıyor . İş parçacığımın dinleyicilere istisnalar göndermek için kullandığı bir sinyal sınıfı tanımlarım. Ayrıca iş parçacıklarından değer döndürmek için de kullanılabilir. Misal:

import threading

class Signal:
    def __init__(self):
        self._subscribers = list()

    def emit(self, *args, **kwargs):
        for func in self._subscribers:
            func(*args, **kwargs)

    def connect(self, func):
        self._subscribers.append(func)

    def disconnect(self, func):
        try:
            self._subscribers.remove(func)
        except ValueError:
            raise ValueError('Function {0} not removed from {1}'.format(func, self))


class WorkerThread(threading.Thread):

    def __init__(self, *args, **kwargs):
        super(WorkerThread, self).__init__(*args, **kwargs)
        self.Exception = Signal()
        self.Result = Signal()

    def run(self):
        if self._Thread__target is not None:
            try:
                self._return_value = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            except Exception as e:
                self.Exception.emit(e)
            else:
                self.Result.emit(self._return_value)

if __name__ == '__main__':
    import time

    def handle_exception(exc):
        print exc.message

    def handle_result(res):
        print res

    def a():
        time.sleep(1)
        raise IOError('a failed')

    def b():
        time.sleep(2)
        return 'b returns'

    t = WorkerThread(target=a)
    t2 = WorkerThread(target=b)
    t.Exception.connect(handle_exception)
    t2.Result.connect(handle_result)
    t.start()
    t2.start()

    print 'Threads started'

    t.join()
    t2.join()
    print 'Done'

Bunun tamamen güvenli bir yöntem olduğunu iddia etmek için iş parçacıkları ile çalışma konusunda yeterli deneyime sahip değilim. Ama benim için çalıştı ve esnekliği seviyorum.


join () işleminden sonra bağlantıyı kesiyor musunuz?
ealeon

Yapmıyorum, ama sanırım bu iyi bir fikir olurdu, bu yüzden takılmayan kullanılmayan şeylere referansınız yok.
RickardSjogren

"handle_exception" hala bir alt iş parçacığının bir parçası olduğunu fark ettim. iplik arayan için geçmek için yol gerekir
ealeon

1

Çıplak istisnalar kullanmak iyi bir uygulama değildir çünkü genellikle pazarlık yaptığınızdan daha fazlasını yakalarsınız.

exceptSADECE ele almak istediğiniz istisnayı yakalamak için değiştirmenizi öneririm . Onu yükseltmenin istenen etkiye sahip olduğunu düşünmüyorum, çünkü TheThreaddışta örneklemeye gittiğinizde try, bir istisna yaratırsa, görev asla gerçekleşmez.

Bunun yerine, sadece uyarmak ve devam etmek isteyebilirsiniz, örneğin:

def run(self):
    try:
       shul.copytree(self.sourceFolder, self.destFolder)
    except OSError, err:
       print err

Sonra bu istisna yakalandığında, orada halledebilirsiniz. Daha sonra dış trybir istisna yakaladığında TheThread, zaten ele aldığınız şey olmayacağını bilirsiniz ve süreç akışınızı izole etmenize yardımcı olur.


1
Peki, bu iş parçacığında bir hata varsa, tam programın bir sorun olduğunu kullanıcıya bildirmesini ve incelikle bitmesini istiyorum. Bu nedenle, ana iş parçacığı tüm istisnaları yakalamak ve işlemek istiyorum. Ancak, TheThread bir istisna atarsa, ana iş parçacığı try / hariç hala onu yakalamayacak sorun hala var. İş parçacığının özel durumu algılamasını ve işlemin başarısız olduğunu gösteren yanlış döndürmesini sağlayabilirim. Bu aynı istenen sonucu elde edecek, ama yine de düzgün bir alt-iplik istisna yakalamak nasıl bilmek istiyorum.
Phanto

1

İş parçacığının istisnasını yakalamanın ve arayan yöntemle iletişim kurmanın basit bir yolu, sözlüğe veya listeye workeryönteme geçmek olabilir .

Örnek (sözlüğü işçi yöntemine geçirme):

import threading

def my_method(throw_me):
    raise Exception(throw_me)

def worker(shared_obj, *args, **kwargs):
    try:
        shared_obj['target'](*args, **kwargs)
    except Exception as err:
        shared_obj['err'] = err

shared_obj = {'err':'', 'target': my_method}
throw_me = "Test"

th = threading.Thread(target=worker, args=(shared_obj, throw_me), kwargs={})
th.start()
th.join()

if shared_obj['err']:
    print(">>%s" % shared_obj['err'])

1

İpliği istisna depolama ile sarın.

import threading
import sys
class ExcThread(threading.Thread):

    def __init__(self, target, args = None):
        self.args = args if args else []
        self.target = target
        self.exc = None
        threading.Thread.__init__(self)

    def run(self):
        try:
            self.target(*self.args)
            raise Exception('An error occured here.')
        except Exception:
            self.exc=sys.exc_info()

def main():
    def hello(name):
        print(!"Hello, {name}!")
    thread_obj = ExcThread(target=hello, args=("Jack"))
    thread_obj.start()

    thread_obj.join()
    exc = thread_obj.exc
    if exc:
        exc_type, exc_obj, exc_trace = exc
        print(exc_type, ':',exc_obj, ":", exc_trace)

main()

0

pygolang içerir sync.WorkGroup , özellikle, ana konuya kökenli alt parçacığı özel yayar. Örneğin:

#!/usr/bin/env python
"""This program demostrates how with sync.WorkGroup an exception raised in
spawned thread is propagated into main thread which spawned the worker."""

from __future__ import print_function
from golang import sync, context

def T1(ctx, *argv):
    print('T1: run ... %r' % (argv,))
    raise RuntimeError('T1: problem')

def T2(ctx):
    print('T2: ran ok')

def main():
    wg = sync.WorkGroup(context.background())
    wg.go(T1, [1,2,3])
    wg.go(T2)

    try:
        wg.wait()
    except Exception as e:
        print('Tmain: caught exception: %r\n' %e)
        # reraising to see full traceback
        raise

if __name__ == '__main__':
    main()

çalıştırıldığında aşağıdakileri verir:

T1: run ... ([1, 2, 3],)
T2: ran ok
Tmain: caught exception: RuntimeError('T1: problem',)

Traceback (most recent call last):
  File "./x.py", line 28, in <module>
    main()
  File "./x.py", line 21, in main
    wg.wait()
  File "golang/_sync.pyx", line 198, in golang._sync.PyWorkGroup.wait
    pyerr_reraise(pyerr)
  File "golang/_sync.pyx", line 178, in golang._sync.PyWorkGroup.go.pyrunf
    f(pywg._pyctx, *argv, **kw)
  File "./x.py", line 10, in T1
    raise RuntimeError('T1: problem')
RuntimeError: T1: problem

Sorunun orijinal kodu sadece:

    wg = sync.WorkGroup(context.background())

    def _(ctx):
        shul.copytree(sourceFolder, destFolder)
    wg.go(_)

    # waits for spawned worker to complete and, on error, reraises
    # its exception on the main thread.
    wg.wait()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.