farklı iş parçacığına atılan istisnayı yakala


110

Yöntemlerimden biri ( Method1) yeni bir iş parçacığı oluşturur. Bu iş parçacığı bir method ( Method2) yürütür ve çalıştırma sırasında bir istisna atılır. Çağıran yöntemle ilgili istisna bilgilerini almam gerekiyor ( Method1)

Ben bu istisna var yakalayabilirsiniz someway Method1ki atılır Method2?

Yanıtlar:


182

In .NET 4 ve üzeri, kullanabileceğiniz Task<T>yeni iş parçacığı oluşturmak yerine sınıfını. Ardından .Exceptions, görev nesnenizdeki özelliği kullanarak istisnalar alabilirsiniz . Bunu yapmanın 2 yolu vardır:

  1. Ayrı bir yöntemde: // Bazı görev dizisindeki istisnayı işlersiniz

    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. Aynı yöntemde: // Çağıranın iş parçacığında istisna işlersiniz

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

Elde ettiğiniz istisnanın olduğunu unutmayın AggregateException. Tüm gerçek istisnalar ex.InnerExceptionsmülkiyet yoluyla elde edilebilir .

In .NET 3.5 aşağıdaki kodu kullanabilirsiniz:

  1. // Çocuğun iş parçacığındaki istisnayı işlersiniz

    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. Veya // Arayanın ileti dizisinde istisna işlersiniz

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

Üzgünüm ama .NET 3.5 kullandığımı söylemeyi unuttum. Anladığım kadarıyla Görev 4.0 şey mi?
Silverlight Student

2
@SilverlightStudent Tamam, gereksinimlerinizi karşılamak için cevabımı güncelledim.
oxilumin

@oxilumin: Teşekkürler ve çok minnettarız. Bir takip sorusu daha. Test () yönteminiz de birkaç bağımsız değişken alıyorsa, bu bağımsız değişkenler için SafeExecute yöntemini nasıl değiştireceksiniz?
Silverlight Student

2
@SilverlightStudent Bu durumda yerine bir lambda geçireceğim Test. Like() => Test(myParameter1, myParameter2)
oxilumin

2
@SilverlightStudent: Güncellendi.
oxilumin

9

Yöntem1'de istisnayı yakalayamazsınız. Bununla birlikte, Method2'deki istisnayı yakalayabilir ve bunu, orijinal yürütme iş parçacığının okuyup birlikte çalışabileceği bir değişkene kaydedebilirsiniz.


Cevabınız için teşekkürler. Dolayısıyla, Yöntem1 Sınıf1'in bir parçasıysa ve bu sınıfta Exception türünde bir değişkenim var. Yöntem2 bir istisna attığında, bu istisna değişkenini Sınıf1'de de ayarlar. Adil bir tasarım gibi geliyor mu? Bu senaryoyu ele almanın en iyi uygulama yolları var mı?
Silverlight Student

Doğru, istisnayı kaydedip daha sonra erişiyorsunuz. Gelecekte çalıştırılan yöntemlerin (özellikle Method2 tamamlandığında geri çağırmaların) bu istisnayı sanki kendileri buna neden olmuş gibi yeniden atması alışılmadık bir durum değildir, ancak bu gerçekten ne istediğinize bağlıdır.
ermau

0

Farklı iş parçacıkları arasında veri paylaşmanın en basit yöntemi shared dataaşağıdaki gibidir (bazıları sözde koddur):

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;
   }
}

Bu yöntem hakkında çok iş parçacığı ile ilgili bu güzel girişte okuyabilirsiniz, ancak bunu O'Reilly book C# 3.0 in a nutshell, kitabın yeni sürümü gibi Google Kitaplar'da da ücretsiz olarak erişilebilen Albahari kardeşler (2007) 'de okumayı tercih ettim. çünkü aynı zamanda güzel ve basit bir örnek kodla iş parçacığı havuzunu, ön plan ve arka plan iş parçacıklarını vb. kapsar. (Sorumluluk reddi: Bu kitabın eskimiş bir kopyasına sahibim)

Bir WinForms uygulaması yapıyorsanız, paylaşılan verilerin kullanımı özellikle kullanışlıdır çünkü WinForm kontrolleri iş parçacığı açısından güvenli değildir. Verileri çalışan iş parçacığından WinForm denetimine geri iletmek için bir geri çağrı kullanmak, ana kullanıcı arabirimi iş parçacığının Invoke()bu denetimi iş parçacığını güvenli hale getirmek için çirkin bir kod gerektirmesi . Bunun yerine paylaşılan verileri ve 0,2 saniye gibi System.Windows.Forms.Timerkısa bir sürede tek iş parçacıklı Intervalkullanarak, çalışan iş parçacığından kontrole olmadan kolayca bilgi gönderebilirsiniz Invoke.


0

Bir entegrasyon test paketinden kontroller içeren öğeleri kullanmak istediğim için özel bir sorun yaşadım, bu yüzden bir STA iş parçacığı oluşturmam gerekiyor. Elde ettiğim kod aşağıdaki gibi, başkalarının da aynı sorunu yaşaması durumunda buraya koyun.

    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) { ... }

Bu, kodun olduğu gibi doğrudan yapıştırılmasıdır. Diğer kullanımlar için, parametre olarak bir eylem veya işlev sağlamayı ve çağrılan yöntemi sabit kodlamak yerine iş parçacığı üzerinde çağırmayı öneririm.

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.