bắt ngoại lệ được đưa vào chuỗi khác


109

Một trong những phương thức của tôi ( Method1) tạo ra một luồng mới. Luồng đó thực thi một phương thức ( Method2) và trong quá trình exectution, một ngoại lệ được ném ra. Tôi cần lấy thông tin ngoại lệ đó trên phương thức gọi ( Method1)

Có cách nào đó tôi có thể bắt được ngoại lệ này trong Method1đó được ném vào Method2không?

Câu trả lời:


181

Trong .NET 4 trở lên, bạn có thể sử dụng Task<T>lớp thay vì tạo luồng mới. Sau đó, bạn có thể nhận được các ngoại lệ bằng cách sử dụng thuộc .Exceptionstính trên đối tượng tác vụ của mình. Có 2 cách để làm điều đó:

  1. Trong một phương thức riêng biệt: // Bạn xử lý ngoại lệ trong chuỗi của một số tác vụ

    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. Trong cùng một phương thức: // Bạn xử lý ngoại lệ trong chuỗi của người gọi

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

Lưu ý rằng ngoại lệ mà bạn nhận được là AggregateException. Tất cả các ngoại lệ thực sự đều có sẵn thông qua ex.InnerExceptionstài sản.

Trong .NET 3.5, bạn có thể sử dụng mã sau:

  1. // Bạn xử lý ngoại lệ trong chuỗi của đứa trẻ

    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. Hoặc // Bạn xử lý ngoại lệ trong chuỗi của người gọi

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

Xin lỗi nhưng tôi đã quên đề cập rằng tôi đang sử dụng .NET 3.5. Theo hiểu biết của tôi Nhiệm vụ là thứ 4.0?
Silverlight Student

2
@SilverlightStudent Ok, tôi vừa cập nhật câu trả lời của mình để đáp ứng yêu cầu của bạn.
oxilumin

@oxilumin: Cảm ơn và đánh giá cao. Một câu hỏi tiếp theo nữa. Nếu phương thức Test () của bạn cũng có một vài đối số, thì bạn sẽ sửa đổi phương thức SafeExecute cho những đối số đó như thế nào?
Silverlight Student

2
@SilverlightStudent Trong trường hợp này, tôi sẽ chuyển một lambda thay vì Test. Thích() => Test(myParameter1, myParameter2)
oxilumin

2
@SilverlightStudent: Đã cập nhật.
oxilumin

9

Bạn không thể bắt được ngoại lệ trong Method1. Tuy nhiên, bạn có thể bắt ngoại lệ trong Method2 và ghi nó vào một biến mà chuỗi thực thi ban đầu sau đó có thể đọc và làm việc với.


Cám ơn phản hồi của bạn. Vì vậy, Nếu Method1 là một phần của Class1 và tôi có một biến kiểu Exception trong lớp đó. Bất cứ khi nào Method2 ném một ngoại lệ, nó cũng đặt biến ngoại lệ đó trong Class1. Nó có vẻ giống như một thiết kế công bằng? Có bất kỳ cách thực hành tốt nhất nào để xử lý tình huống này không?
Silverlight Student

Chính xác, bạn chỉ cần lưu trữ ngoại lệ và truy cập nó sau. Không có gì lạ khi các phương thức chạy trong tương lai (đặc biệt là các lệnh gọi lại khi Method2 hoàn tất) sau đó lặp lại ngoại lệ đó như thể chính chúng đã gây ra nó, nhưng điều này thực sự phụ thuộc vào những gì bạn muốn.
ermau

0

Phương pháp đơn giản nhất để chia sẻ dữ liệu giữa các luồng khác nhau shared datanhư sau (một số là mã giả):

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

Bạn có thể đọc về phương pháp này trong phần giới thiệu hay về đa luồng này , tuy nhiên, tôi thích đọc về phương pháp này trong O'Reilly book C# 3.0 in a nutshell, của anh em Albahari (2007), cũng có thể truy cập miễn phí trên Google Sách, giống như phiên bản mới hơn của sách, bởi vì nó cũng bao gồm gộp luồng, luồng nền trước so với nền sau, v.v., với mã ví dụ đẹp và đơn giản. (Tuyên bố từ chối trách nhiệm: Tôi sở hữu một bản sao cũ của cuốn sách này)

Trong trường hợp bạn đang tạo một ứng dụng WinForms, việc sử dụng dữ liệu được chia sẻ đặc biệt hữu ích, vì các điều khiển WinForm không an toàn theo luồng. Sử dụng lệnh gọi lại để chuyển dữ liệu từ chuỗi công nhân trở lại điều khiển WinForm, chuỗi giao diện người dùng chính cần có mã xấu Invoke()để làm cho chuỗi điều khiển đó an toàn. Thay vào đó, sử dụng dữ liệu được chia sẻ và luồng đơn System.Windows.Forms.Timer, chỉ với Interval0,2 giây, bạn có thể dễ dàng gửi thông tin từ luồng công nhân tới bộ điều khiển mà không cần Invoke.


0

Tôi gặp một vấn đề cụ thể là tôi muốn sử dụng các mục, có chứa các điều khiển, từ một bộ kiểm tra tích hợp, vì vậy phải tạo một chuỗi STA. Mã tôi đã kết thúc như sau, đặt ở đây trong trường hợp những người khác có cùng vấn đề.

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

Đây là cách dán trực tiếp mã nguyên trạng. Đối với các mục đích sử dụng khác, tôi khuyên bạn nên cung cấp một hành động hoặc hàm dưới dạng tham số và gọi nó trên luồng thay vì mã hóa cứng phương thức được gọi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.