menangkap pengecualian yang dilemparkan ke utas berbeda

110

Salah satu metode saya ( Method1) memunculkan utas baru. Utas itu mengeksekusi metode ( Method2) dan selama pelaksanaan pengecualian dilempar. Saya perlu mendapatkan informasi pengecualian itu pada metode pemanggilan ( Method1)

Apakah bagaimanapun saya bisa menangkap pengecualian ini Method1yang dilemparkan Method2?

Mahasiswa Silverlight
sumber

Jawaban:

182

Di .NET 4 dan yang lebih baru, Anda dapat menggunakan Task<T>kelas daripada membuat utas baru. Kemudian Anda bisa mendapatkan pengecualian menggunakan .Exceptionsproperti pada objek tugas Anda. Ada 2 cara untuk melakukannya:

  1. Dalam metode terpisah: // Anda memproses pengecualian di thread beberapa tugas

    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Test);
            task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
            task.Start();
            Console.ReadLine();
        }
    
        static int Test()
        {
            throw new Exception();
        }
    
        static void ExceptionHandler(Task<int> task)
        {
            var exception = task.Exception;
            Console.WriteLine(exception);
        }
    }
    
  2. Dalam metode yang sama: // Anda memproses pengecualian di utas pemanggil

    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Test);
            task.Start();
    
            try
            {
                task.Wait();
            }
            catch (AggregateException ex)
            {
                Console.WriteLine(ex);    
            }
    
            Console.ReadLine();
        }
    
        static int Test()
        {
            throw new Exception();
        }
    }
    

Perhatikan bahwa pengecualian yang Anda dapatkan adalah AggregateException. Semua pengecualian nyata tersedia melalui ex.InnerExceptionsproperti.

Di .NET 3.5 Anda dapat menggunakan kode berikut:

  1. // Anda memproses pengecualian di utas anak

    class Program
    {
        static void Main(string[] args)
        {
            Exception exception = null;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), Handler));
            thread.Start();            
    
            Console.ReadLine();
        }
    
        private static void Handler(Exception exception)
        {        
            Console.WriteLine(exception);
        }
    
        private static void SafeExecute(Action test, Action<Exception> handler)
        {
            try
            {
                test.Invoke();
            }
            catch (Exception ex)
            {
                Handler(ex);
            }
        }
    
        static void Test(int a, int b)
        {
            throw new Exception();
        }
    }
    
  2. Atau // Anda memproses pengecualian di utas pemanggil

    class Program
    {
        static void Main(string[] args)
        {
            Exception exception = null;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), out exception));
    
            thread.Start();            
    
            thread.Join();
    
            Console.WriteLine(exception);    
    
            Console.ReadLine();
        }
    
        private static void SafeExecute(Action test, out Exception exception)
        {
            exception = null;
    
            try
            {
                test.Invoke();
            }
            catch (Exception ex)
            {
                exception = ex;
            }
        }
    
        static void Test(int a, int b)
        {
            throw new Exception();
        }
    }
    
oxilumin
sumber
Maaf, tetapi saya lupa menyebutkan bahwa saya menggunakan .NET 3.5. Sesuai pemahaman saya Task adalah 4.0 hal?
Silverlight Student
2
@SilverlightStudent Ok, saya baru saja memperbarui jawaban saya untuk memenuhi kebutuhan Anda.
oxilumin
@oxilumin: Terima kasih dan sangat dihargai. Satu pertanyaan lanjutan lagi. Jika metode Test () Anda juga membutuhkan beberapa argumen, lalu bagaimana Anda akan mengubah metode SafeExecute untuk argumen tersebut?
Silverlight Student
2
@SilverlightStudent Dalam hal ini saya akan memberikan lambda, bukan Test. Seperti() => Test(myParameter1, myParameter2)
oxilumin
2
@SilverlightStudent: Diperbarui.
oxilumin
9

Anda tidak dapat menangkap pengecualian di Metode1. Namun, Anda dapat menangkap pengecualian di Metode2 dan merekamnya ke variabel yang kemudian dapat dibaca dan dikerjakan oleh utas eksekusi asli.

ermau
sumber
Terimakasih atas tanggapan Anda. Jadi Jika Metode1 adalah bagian dari Kelas1 dan saya memiliki variabel tipe Pengecualian di kelas itu. Setiap kali Method2 melontarkan pengecualian, itu menetapkan variabel pengecualian itu di Kelas1 juga. Apakah ini terdengar seperti desain yang adil? Apakah ada cara praktik terbaik untuk menangani skenario ini?
Silverlight Student
Benar, Anda cukup menyimpan pengecualian dan mengaksesnya nanti. Tidak jarang metode yang dijalankan di masa mendatang (terutama callback ketika Method2 selesai) untuk kemudian menampilkan kembali pengecualian tersebut seolah-olah mereka sendirilah yang menyebabkannya, tetapi ini benar-benar tergantung pada apa yang Anda inginkan.
ermau
0

Metode paling sederhana untuk berbagi data di antara utas yang berbeda adalah shared datasebagai berikut (beberapa adalah kode pseudo):

class MyThread
{
   public string SharedData;

   public void Worker()
   {
      ...lengthy action, infinite loop, etc...
      SharedData = "whatever";
      ...lengthy action...
      return;
   }
}

class Program
{
   static void Main()
   {
      MyThread m = new MyThread();
      Thread WorkerThread = new Thread(m.Worker);
      WorkerThread.Start();

      loop//or e.g. a Timer thread
      {
         f(m.SharedData);
      }
      return;
   }
}

Anda dapat membaca tentang metode ini dalam pengantar yang bagus tentang multithreading ini , namun, saya lebih suka membaca tentang ini di O'Reilly book C# 3.0 in a nutshell, oleh saudara-saudara Albahari (2007), yang juga dapat diakses secara bebas di Google Buku, seperti versi buku yang lebih baru, karena ini juga mencakup kumpulan thread, thread latar depan versus latar belakang, dll., dengan kode contoh yang bagus dan sederhana. (Penafian: Saya memiliki salinan buku ini yang sudah usang)

Jika Anda membuat aplikasi WinForms, penggunaan data bersama sangat berguna, karena kontrol WinForm tidak aman untuk thread. Menggunakan callback untuk meneruskan data dari thread pekerja kembali ke kontrol WinForm, thread UI utama memerlukan kode jelek Invoke()untuk membuat kontrol thread tersebut aman. Menggunakan data bersama sebagai gantinya, dan single-threaded System.Windows.Forms.Timer, dengan waktu singkat Intervalkatakanlah 0,2 detik, Anda dapat dengan mudah mengirim informasi dari thread pekerja ke kontrol tanpa Invoke.

Roland
sumber
0

Saya memiliki masalah khusus karena saya ingin menggunakan item, yang berisi kontrol, dari rangkaian pengujian integrasi, jadi harus membuat thread STA. Kode yang saya dapatkan adalah sebagai berikut, taruh di sini jika orang lain memiliki masalah yang sama.

    public Boolean? Dance(String name) {

        // Already on an STA thread, so just go for it
        if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) return DanceSTA(name);

        // Local variable to hold the caught exception until the caller can rethrow
        Exception lException = null;

        Boolean? lResult = null;

        // A gate to hold the calling thread until the called thread is done
        var lGate = new ManualResetEvent(false);

        var lThreadStart = new ThreadStart(() => {
            try {
                lResult = DanceSTA(name);
            } catch (Exception ex) {
                lException = ex;
            }
            lGate.Set();
        });

        var lThread = new Thread(lThreadStart);
        lThread.SetApartmentState(ApartmentState.STA);
        lThread.Start();

        lGate.WaitOne();

        if (lException != null) throw lException;

        return lResult;
    }

    public Boolean? DanceSTA(String name) { ... }

Ini adalah tempel langsung dari kode apa adanya. Untuk kegunaan lain, saya akan merekomendasikan menyediakan tindakan atau fungsi sebagai parameter dan menjalankannya di utas alih-alih melakukan pengkodean keras metode yang disebut.

Richard Petheram
sumber