পাইথনের কলার থ্রেডে একটি থ্রেডের ব্যতিক্রম ধরা


207

আমি পাইথনে খুব নতুন এবং সাধারণভাবে মাল্টিথ্রেডেড প্রোগ্রামিং। মূলত, আমার কাছে একটি স্ক্রিপ্ট রয়েছে যা ফাইলগুলি অন্য কোনও স্থানে অনুলিপি করবে। আমি এটি অন্য থ্রেডে রাখতে চাই যাতে ....স্ক্রিপ্টটি এখনও চলছে কিনা তা বোঝাতে আমি আউটপুট করতে পারি ।

আমার যে সমস্যাটি হচ্ছে তা হ'ল যদি ফাইলগুলি অনুলিপি করা না যায় তবে এটি একটি ব্যতিক্রম ছুঁড়ে দেবে। মূল থ্রেডে চললে এটি ঠিক আছে; তবে, নিম্নলিখিত কোডটি কাজ করে না:

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

নিজেই থ্রেড ক্লাসে, আমি ব্যতিক্রমটি আবার ছুঁড়ে ফেলার চেষ্টা করেছি, তবে এটি কার্যকর হয় না। আমি এখানকার লোকদের অনুরূপ প্রশ্ন জিজ্ঞাসা করতে দেখেছি, তবে তারা সকলেই আমি যা করার চেষ্টা করছি তার চেয়ে বেশি নির্দিষ্ট কিছু করছে বলে মনে হয় (এবং প্রস্তাবিত সমাধানগুলি আমি যথেষ্ট বুঝতে পারি না)। আমি লোকদের ব্যবহারের উল্লেখ করতে দেখেছি sys.exc_info(), তবে কোথায় বা কীভাবে এটি ব্যবহার করতে হয় তা আমি জানি না।

সমস্ত সাহায্যের প্রশংসা করা হয়!

সম্পাদনা: থ্রেড শ্রেণীর জন্য কোডটি নীচে রয়েছে:

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

ভিতরে কী ঘটছে সে সম্পর্কে আপনি আরও অন্তর্দৃষ্টি দিতে পারেন TheThread? কোড নমুনা সম্ভবত?
জ্যাথানিজম

অবশ্যই। আমি কিছু বিবরণ অন্তর্ভুক্ত করতে উপরের আমার প্রতিক্রিয়াটি সম্পাদনা করব।
ফ্যান্টো

1
আপনি কি এটিকে চারদিকে বদলানোর বিষয়টি বিবেচনা করেছেন তাই মূল থ্রেডটি বিট যা স্টাফ করে এবং অগ্রগতি সূচকটি স্পাড থ্রেডে থাকে?
ড্যান হেড

1
ড্যান হেড, আপনি কি প্রথমে "..." ফাংশনটি ছড়িয়ে দিয়ে তারপরে অনুলিপিটির মূল ক্রিয়াকলাপটি চালাচ্ছেন? এটি কাজ করতে পারে এবং ব্যতিক্রমের বিষয়টি এড়াতে পারে। তবে, আমি এখনও অজগরটি কীভাবে সঠিকভাবে থ্রেড করতে শিখতে চাই।
ফ্যান্টো

উত্তর:


114

সমস্যাটি thread_obj.start()তাৎক্ষণিকভাবে ফিরে আসে। আপনি যে সন্তানের থ্রেড তৈরি করেছেন তা তার নিজস্ব স্ট্যাকের সাথে তার নিজস্ব প্রসঙ্গে চালায়। সেখানে ঘটে যাওয়া কোনও ব্যতিক্রম শিশু থ্রেডের প্রসঙ্গে এবং এটি তার নিজস্ব স্ট্যাকের মধ্যে রয়েছে। এই তথ্যটি প্যারেন্ট থ্রেডের সাথে যোগাযোগ করার জন্য আমি এখনই ভাবতে পারি এটি হ'ল কোনও ধরণের বার্তা প্রেরণ করে, যাতে আপনি এটি সন্ধান করতে পারেন।

আকারের জন্য এটি ব্যবহার করে দেখুন:

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
লুপের সময় কেন এই কুশ্রীটির পরিবর্তে থ্রেডে যোগ দিচ্ছেন না? multiprocessingসমতুল্য দেখুন : gist.github.com/2311116
schlamar

1
কেন EventHook প্যাটার্ন ব্যবহার করছেন না stackoverflow.com/questions/1092531/event-system-in-python/... @Lasse উত্তর ভিত্তিক? লুপ জিনিসটার চেয়ে?
আন্দ্রে মীরাস

1
কোনও ত্রুটি ফিরে পাওয়ার জন্য ক্যু হ'ল সেরা বাহন নয়, যদি না আপনি সেগুলির একটি সম্পূর্ণ সারি রাখতে চান। আরও অনেক ভাল নির্মাণ থ্রেডিং করা হচ্ছে
ventদু

1
এটি আমার কাছে অনিরাপদ বলে মনে হচ্ছে। যখন থ্রেড পর একটি ব্যতিক্রম অধিকার উত্থাপন কি হবে bucket.get()উত্থাপনQueue.Empty ? তারপরে থ্রেডটি join(0.1)সম্পূর্ণ হবে এবং isAlive() is False, এবং আপনি নিজের ব্যতিক্রম মিস করেছেন।
স্টিভ

1
Queueএই সাধারণ ক্ষেত্রে অপ্রয়োজনীয় - আপনি ExcThreadযতক্ষণ না নিশ্চিত হন ততক্ষণ আপনি কেবলমাত্র ব্যতিক্রম সম্পর্কিত তথ্য হিসাবে সম্পত্তি হিসাবে সংরক্ষণ করতে পারেনrun() ব্যতিক্রমের ঠিক পরে সম্পূর্ণ (যতক্ষণ না এটি এই সাধারণ উদাহরণে ঘটে) । তারপরে আপনি কেবল (বা সময়কালের) পরে ব্যতিক্রমটি পুনরায় উত্থাপন করুন t.join()। কোনও সিঙ্ক্রোনাইজেশন সমস্যা নেই কারণ join()এটি নিশ্চিত করে যে থ্রেডটি সম্পন্ন হয়েছে। নিচে Rok Strniša দ্বারা উত্তর দেখার stackoverflow.com/a/12223550/126362
ejm

42

concurrent.futuresমডিউল এটা সহজ পৃথক থ্রেড (অথবা প্রসেস) এ কাজ করতে এবং কোন ফলে ব্যতিক্রম হ্যান্ডেল করে তোলে:

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.futuresপাইথন ৩.২ এর সাথে অন্তর্ভুক্ত রয়েছে এবং পূর্ববর্তী সংস্করণগুলির ব্যাকপোর্টেড futuresমডিউল হিসাবে এটি উপলব্ধ ।


5
যদিও এটি ওপি যা বলেছিল ঠিক তেমন করে না, এটি আমার প্রয়োজনীয় ইঙ্গিতটি। ধন্যবাদ.
ম্যাড পদার্থবিদ

2
এবং সঙ্গে concurrent.futures.as_completed:, আপনি অবিলম্বে জানানো পারে ব্যতিক্রম উত্থাপিত হয় stackoverflow.com/questions/2829329/...
সিরো Santilli郝海东冠状病六四事件法轮功

1
এই কোডটি মূল থ্রেডটি ব্লক করছে। আপনি কীভাবে এটি অবিচ্ছিন্নভাবে করবেন?
নিকোলে শিন্দারভ

40

এই প্রশ্নের অনেক অদ্ভুত জটিল উত্তর রয়েছে। আমি কি এটির চেয়ে বেশি বোঝাপড়া করছি, কারণ এটি আমার কাছে বেশিরভাগ জিনিসের পক্ষে যথেষ্ট বলে মনে হয়।

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

আপনি যদি নিশ্চিত হন যে আপনি কেবল পাইথনের এক বা অন্য সংস্করণে চলছেন তবে আপনি run()পদ্ধতিটি কেবলমাত্র ম্যাংলেড সংস্করণে হ্রাস করতে পারবেন (যদি আপনি কেবল পাইথনের সংস্করণে 3 এর আগে চলছেন), বা কেবল পরিষ্কার সংস্করণ (যদি আপনি কেবল পাইথনের সংস্করণগুলি 3 দিয়ে শুরু করেন)।

ব্যবহারের উদাহরণ:

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()

আপনি যোগদানের পরে আপনি অন্য থ্রেডে উত্থাপিত ব্যতিক্রমটি দেখতে পাবেন।

যদি আপনি sixকেবল পাইথন 3 ব্যবহার করেন বা ব্যতিক্রমটি পুনরায় উত্থাপিত হয় তখন আপনি স্ট্যাক ট্রেস তথ্যটি উন্নত করতে পারেন। যোগদানের পয়েন্টে কেবল স্ট্যাকের পরিবর্তে, আপনি অভ্যন্তরীণ ব্যতিক্রমটিকে নতুন বাইরের ব্যতিক্রমতে আবদ্ধ করতে পারেন এবং স্ট্যাকের চিহ্ন দুটিই পেতে পারেন

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

অথবা

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

1
এই উত্তরটি কেন বেশি জনপ্রিয় নয় তা আমি নিশ্চিত নই। এখানে আরও কিছু রয়েছে যা সাধারণ প্রচারও করে, তবে একটি শ্রেণি বাড়ানো এবং ওভাররাইডিংয়ের প্রয়োজন। এটি কেবলমাত্র অনেকেই যা প্রত্যাশা করে তা করে, এবং কেবল থ্রেড থেকে প্রাগেটিং থ্রেডে পরিবর্তনের প্রয়োজন। এবং 4 টি স্পেস ট্যাব যাতে আমার অনুলিপি / পেস্টগুলি তুচ্ছ ছিল :-) ... আমি কেবলমাত্র উন্নতি হিসাবে সিক্স.রেজ_ফ্রোম () ব্যবহার করছি যাতে আপনি কেবল স্ট্যাকের পরিবর্তে স্ট্যাকের চিহ্নগুলির একটি সুন্দর নেস্টেট সেট পান the পুনরুদ্ধারের সাইট।
aggieNick02

আপনাকে অনেক ধন্যবাদ. খুব সহজ সমাধান।
সোনুলোহানী

আমার সমস্যাটি হ'ল আমার একাধিক সন্তানের থ্রেড রয়েছে। যোগদানগুলি ক্রমানুসারে কার্যকর করা হয় এবং পরে যুক্ত হওয়া থ্রেড থেকে ব্যতিক্রম উত্থাপিত হতে পারে। আমার সমস্যার কি সরল সমাধান আছে? একসাথে যোগদান যোগদান?
চুয়ান

ধন্যবাদ, এটি পুরোপুরি কাজ করে! কেন এটি সরাসরি পাইথন দ্বারা পরিচালিত হচ্ছে না তা নিশ্চিত নয়…
জিজি।

এটি সর্বাধিক দরকারী উত্তর হিসাবে সংজ্ঞায়িত করা হয়, অন্যদের তুলনায় সহজ এই সরলতা আরও সাধারণ। এটি একটি প্রকল্পে ব্যবহার করবে!
কনস্ট্যান্টিন সেকেরেশ

30

যদিও আলাদা থ্রেডে ফেলে দেওয়া ব্যতিক্রমটি সরাসরি ধরা সম্ভব নয়, এই কার্যকারিতাটির খুব কাছাকাছি কিছু পাওয়ার জন্য এখানে একটি স্বচ্ছ কোড রয়েছে। আপনার সন্তানের থ্রেডের ExThreadপরিবর্তে ক্লাসটি সাবক্লাস করতে হবে threading.Threadএবং পরিবর্তে প্যারেন্ট থ্রেডকে অবশ্যই child_thread.join_with_exception()পদ্ধতিটি কল করতে হবেchild_thread.join() কাজ শেষ হওয়ার জন্য অপেক্ষা ।

এই বাস্তবায়নের প্রযুক্তিগত বিবরণ: যখন শিশু থ্রেড একটি ব্যতিক্রম ছুঁড়ে ফেলে, তখন এটি একটি এর মাধ্যমে পিতামাতার কাছে পৌঁছে Queueদেওয়া হয় এবং আবার পিতামাতার থ্রেডে ফেলে দেওয়া হয়। লক্ষ্য করুন যে এই পদ্ধতির জন্য অপেক্ষা করতে ব্যস্ত নেই।

#!/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
আপনি ধরতে চান না BaseException, না Exception? আপনি যা করছেন তা ব্যতিক্রমকে একের বাইরে Threadএবং অন্যটিতে প্রচার করা। এখনই, আইই, এ KeyboardInterrupt, এটি যদি কোনও পটভূমির থ্রেডে উত্থাপিত হয় তবে চুপচাপ উপেক্ষা করা হবে।
আর্টঅফ ওয়ারফেয়ার

join_with_exceptionমৃত থ্রেডে দ্বিতীয়বার ডাকলে অনির্দিষ্টকালের জন্য স্তব্ধ হয়ে যায়। স্থির করুন: github.com/fraserharris/threading-extensions/blob/master/…
ফ্রেজার হ্যারিস

আমি Queueপ্রয়োজনীয় মনে করি না ; @ সান্তার উত্তরে আমার মন্তব্য দেখুন। আপনি নীচের Rok Strniša এর উত্তর ভালো কিছু করার জন্য এটি ডাউন প্রক্রিয়া সহজ করতে stackoverflow.com/a/12223550/126362
ejm

22

যদি কোনও থ্রেডে ব্যতিক্রম ঘটে থাকে তবে সবচেয়ে ভাল উপায় হ'ল কলার থ্রেডে এটি পুনরায় উত্থাপন করা join। আপনি বর্তমানে এই sys.exc_info()ফাংশনটি ব্যবহার করে ব্যতিক্রম হ্যান্ডেল করা হচ্ছে সে সম্পর্কে তথ্য পেতে পারেন । এই তথ্যটি সহজেই থ্রেড অবজেক্টের সম্পত্তি হিসাবে সংরক্ষণ করা যেতে পারে যতক্ষণ না joinবলা হয়, যে বিন্দুতে এটি পুনরায় উত্থাপিত হতে পারে।

মনে রাখবেন, যে Queue.Queue(অন্যান্য উত্তর প্রস্তাবিত) এই সহজ ক্ষেত্রে যেখানে থ্রেড প্রয়োজন নেই ছোঁড়ার সর্বাধিক 1 ব্যতিক্রম এবং একটি ব্যতিক্রম নিক্ষেপ পর ডান সম্পন্ন হয়ে । থ্রেডটি সম্পূর্ণ হওয়ার জন্য কেবল অপেক্ষা করে আমরা বর্ণের পরিস্থিতি এড়িয়ে চলি।

উদাহরণস্বরূপ, প্রসারিত ExcThread(নীচে), ওভাররাইডিং excRun(পরিবর্তে run)।

পাইথন 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]

পাইথন 3.x:

3 টি আর্গুমেন্ট ফর্মটি raiseপাইথন 3 এ চলে গেছে, সুতরাং শেষ লাইনটি এতে পরিবর্তন করুন:

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

2
আপনি কেন সুপারিং (এক্সট্রেড, স্ব) এর পরিবর্তে থ্রেডিং.ত্রেড.জাইন (স্ব) ব্যবহার করবেন? জয়ন ()?
রিচার্ড মোহন

9

concurrent.futures.as_completed

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

নিম্নলিখিত সমাধান:

  • একটি ব্যতিক্রম কল হলে তাত্ক্ষণিক মূল থ্রেডে ফিরে আসে
  • অতিরিক্ত ব্যবহারকারীর সংজ্ঞায়িত শ্রেণীর প্রয়োজন নেই কারণ এটির প্রয়োজন নেই:
    • একটি স্পষ্ট Queue
    • আপনার কাজের থ্রেডের চারপাশে অন্য একটি যুক্ত করতে

উৎস:

#!/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()))

সম্ভাব্য আউটপুট:

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

দুর্ভাগ্যক্রমে অন্যদের বাতিল করতে ফিউচারগুলিকে হত্যা করা সম্ভব নয় কারণ এটির ব্যর্থতা:

আপনি যদি এমন কিছু করেন:

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

তারপরে এটি withধরা পড়ে এবং চালিয়ে যাওয়ার আগে দ্বিতীয় থ্রেডটি শেষ হওয়ার অপেক্ষা করে। নিম্নলিখিতগুলি একইভাবে আচরণ করে:

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

যেহেতু future.result()যদি কোনও ঘটনা ঘটে থাকে তবে ব্যতিক্রমটি পুনরায় উত্থাপন করে।

আপনি যদি পুরো পাইথন প্রক্রিয়াটি প্রস্থান করতে চান তবে আপনি এখান থেকে দূরে সরে যেতে পারেন os._exit(0), তবে এর সম্ভাব্য অর্থ হল আপনার একটি চুল্লি প্রয়োজন need

নিখুঁত ব্যতিক্রম শব্দার্থবিজ্ঞান সহ কাস্টম ক্লাস

আমি এখানে নিজের জন্য নিখুঁত ইন্টারফেস কোডিং শেষ করেছি: একবারে সর্বাধিক সংখ্যক থ্রেড সীমাবদ্ধ করার সঠিক উপায়? বিভাগ "ত্রুটি পরিচালনা করার সাথে সারি উদাহরণ"। এই শ্রেণীর লক্ষ্য ছিল উভয়ই সুবিধাজনক, এবং আপনাকে জমা দেওয়া এবং ফলাফল / ত্রুটি পরিচালনার উপর সম্পূর্ণ নিয়ন্ত্রণ দেয়।

পাইথন 3.6.7, উবুন্টু 18.04 এ পরীক্ষিত।


4

এটি একটি বাজে সামান্য সমস্যা ছিল এবং আমি আমার সমাধানটি ফেলে দিতে চাই I সারি / ইভেন্ট লুপ পদ্ধতির সাজানোর একটি নির্দিষ্ট বাস্তবায়নের সাথে আপনাকে সংযুক্ত করে। একযোগে ফিউচার সোর্স কোডটি তবে কেবলমাত্র 1000 লাইন এবং বোঝা সহজ । এটি আমাকে সহজেই আমার সমস্যার সমাধান করার অনুমতি দেয়: বেশি সেটআপ ছাড়াই অ্যাড-হক কর্মী থ্রেড তৈরি করুন এবং মূল থ্রেডে ব্যতিক্রমগুলি ধরতে সক্ষম হবেন।

আমার সমাধানটি সমবর্তী ফিউচার এপিআই এবং থ্রেডিং এপিআই ব্যবহার করে। এটি আপনাকে এমন একটি কর্মী তৈরি করতে দেয় যা আপনাকে থ্রেড এবং ভবিষ্যত উভয়ই দেয়। এইভাবে, আপনি ফলাফলটির জন্য অপেক্ষা করতে থ্রেডে যোগ দিতে পারেন:

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

... বা আপনি কর্মীকে কেবল কলব্যাক পাঠাতে দিতে পারেন:

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

... বা ইভেন্টটি শেষ না হওয়া পর্যন্ত আপনি লুপ করতে পারেন:

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)

কোডটি এখানে:

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())

... এবং পরীক্ষার ফাংশন:

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

2

থ্রেডিংয়ের নুবি হিসাবে ম্যাটিউজ কোবসের কোড (উপরে) কীভাবে প্রয়োগ করতে হয় তা বুঝতে আমার অনেক সময় লেগেছে। এটি কীভাবে ব্যবহার করতে হয় তা বুঝতে সহায়তা করার জন্য এখানে একটি স্পষ্ট বর্ণিত সংস্করণ।

#!/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

অনুরূপভাবে রিকার্ডজোগ্রেনের কুইউ ব্যতীত, সিস্ট ইত্যাদি নয় তবে কিছু শ্রোতা সংকেত ছাড়াই: সরাসরি একটি ব্যতিক্রম হ্যান্ডলার কার্যকর করুন যা ব্লক ব্যতীত অনুরূপ।

#!/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

কেবল স্ব ._ক্যালব্যাক এবং রান ইন ব্লক ব্যতীত সাধারণ থ্রেডিংয়ের অতিরিক্ত additional থ্রেড।


2

আমি জানি আমি এখানে পার্টিতে কিছুটা দেরি করেছি তবে আমার খুব অনুরূপ সমস্যা হচ্ছে তবে এটিতে জিইউআই হিসাবে টিন্টার ব্যবহার করা অন্তর্ভুক্ত ছিল এবং মেইনলুপটি .join () এর উপর নির্ভরশীল কোনও সমাধান ব্যবহার করা অসম্ভব করে তুলেছিল। অতএব আমি মূল প্রশ্নের EDIT এ প্রদত্ত সমাধানটি অভিযোজিত করেছি, তবে অন্যের জন্য এটি বোঝা সহজ করার জন্য এটি আরও সাধারণ করে তুলেছি।

এখানে নতুন থ্রেড ক্লাসটি কার্যকর রয়েছে:

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()

অবশ্যই আপনি এটি সর্বদা লগিং থেকে ব্যতিক্রমটিকে অন্য কোনও উপায়ে পরিচালনা করতে পারেন যেমন এটিকে মুদ্রণ করা, বা কনসোলে আউটপুট রাখা।

এটি আপনাকে কোনও বিশেষ পরিবর্তন ছাড়াই থ্রেড ক্লাসের মতো ঠিক এক্সেকশনথ্রেড ক্লাসটি ব্যবহার করতে দেয়।


1

আমি পছন্দ করি এমন একটি পদ্ধতি পর্যবেক্ষক প্যাটার্নের ভিত্তিতে । আমি একটি সংকেত শ্রেণির সংজ্ঞা দিই যা আমার থ্রেড শ্রোতাদের ব্যতিক্রম নির্ধারণ করতে ব্যবহার করে। এটি থ্রেড থেকে মানগুলি ফেরত দিতেও ব্যবহার করা যেতে পারে। উদাহরণ:

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'

এটি সম্পূর্ণ নিরাপদ পদ্ধতি বলে দাবি করার জন্য আমার কাছে থ্রেড নিয়ে কাজ করার যথেষ্ট অভিজ্ঞতা নেই। তবে এটি আমার পক্ষে কাজ করেছে এবং আমি নমনীয়তাটি পছন্দ করি।


আপনি যোগদানের পরে সংযোগ বিচ্ছিন্ন ()?
ইলেওন

আমি না, তবে আমি অনুমান করি এটি একটি ভাল ধারণা হবে সুতরাং আপনার অবহেলিত স্টাফগুলির বিষয়ে উল্লেখ নেই।
রিকার্ডসোগ্রেন

আমি লক্ষ্য করেছি যে "হ্যান্ডেল_ এক্সসেপশন" এখনও একটি শিশু সুতোর অংশ। এটি থ্রেড কলারের কাছে
পৌঁছানোর উপায়টি দরকার

1

নগ্ন ব্যতিক্রমগুলি ব্যবহার করা ভাল অভ্যাস নয় কারণ আপনি সাধারণত দর কষাকষির চেয়ে বেশি ধরেন।

আমি exceptকেবলমাত্র ব্যতিক্রমগুলি আপনি পরিচালনা করতে চান তা ধরতে পরিবর্তনের পরামর্শ দেব would আমি মনে করি না যে এটি উত্থাপনের কাঙ্ক্ষিত প্রভাব রয়েছে, কারণ আপনি যখন TheThreadবাইরের দিকে তাত্ক্ষণিকভাবে যান try, যদি এটি একটি ব্যতিক্রম উত্থাপন করে, অ্যাসাইনমেন্টটি কখনই ঘটবে না।

পরিবর্তে আপনি সম্ভবত এটি সম্পর্কে সর্তক হয়ে উঠতে চান এবং যেমন:

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

তারপরে যখন সেই ব্যতিক্রম ধরা পড়ে, আপনি সেখানে এটি পরিচালনা করতে পারেন। তারপরে যখন বাইরের tryকোনও ব্যতিক্রম ধরা পড়ে TheThread, আপনি জানেন যে এটি আপনি ইতিমধ্যে পরিচালনা করেছেন এমনটি হবে না এবং আপনার প্রক্রিয়া প্রবাহকে বিচ্ছিন্ন করতে আপনাকে সহায়তা করবে।


1
ঠিক আছে, যদি এই থ্রেডটিতে কিছুতেই ত্রুটি থাকে তবে আমি সম্পূর্ণ প্রোগ্রামটি ব্যবহারকারীকে জানাতে চাই যে এখানে একটি সমস্যা আছে এবং চূড়ান্তভাবে শেষ হয়েছে। যে কারণে, আমি চাই যে মূল থ্রেডটি সমস্ত ব্যতিক্রমগুলি ধরা এবং পরিচালনা করবে। তবে, সমস্যাটি এখনও রয়েছে যেখানে থিথ্রেড যদি ব্যতিক্রম ছুঁড়ে ফেলে তবে মূল থ্রেডের চেষ্টা / বাদে এখনও তা ধরবে না। আমি থ্রেডটি ব্যতিক্রমটি সনাক্ত করতে এবং একটি মিথ্যা ফিরিয়ে দিতে পারবো যে ইঙ্গিত দিয়েছিল যে অপারেশন ব্যর্থ হয়েছিল। এটি একই পছন্দসই ফলাফল অর্জন করবে, তবে আমি কীভাবে সাব-থ্রেড ব্যতিক্রমটি সঠিকভাবে ধরতে হবে তা জানতে চাই।
ফ্যান্টো

1

থ্রেডের ব্যতিক্রম ধরা এবং কলার পদ্ধতির সাথে যোগাযোগ করার একটি সহজ উপায় অভিধান বা workerপদ্ধতিতে কোনও তালিকা পাস করার মাধ্যমে হতে পারে ।

উদাহরণ (কর্মী পদ্ধতিতে অভিধান পাস করা):

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

ব্যতিক্রম স্টোরেজ সহ থ্রেড মোড়ানো।

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 উপলব্ধ sync.WorkGroup যা, বিশেষ করে, মূল থ্রেডে উত্পন্ন হওয়া শ্রমিক থ্রেড থেকে ব্যতিক্রম propagates। উদাহরণ স্বরূপ:

#!/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()

রান করার সময় নিম্নলিখিতটি দেয়:

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

প্রশ্ন থেকে মূল কোডটি কেবলমাত্র হবে:

    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()
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.