2009-12-11 50 views
10

một Strange rằng tôi không còn nhận được, là thế này:ngoại lệ từ lambda biểu thức

Say,

try 
{ 
    stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length, 
     SocketFlags.None, ar => stateClient.Socket.EndSend(ar), stateClient); 
} 
catch (SocketException ex) 
{ 
    // Handle SocketException. 
} 
catch (ObjectDisposedException ex) 
{ 
    // Handle ObjectDisposedException. 
} 

Tôi không hiểu tại sao biểu thức lambda trả về với ObjectDisposedException không bắt !? Tôi đã đi sâu hơn vào lambdas và tôi không thể hiểu nó. Đó là về phạm vi của lambda? Phạm vi biến? Vấn đề về chủ đề? Tôi biết lambda không có đa luồng bởi bản chất của họ, nhưng như bạn có thể thấy sự trở lại đến từ một sợi khác được tạo ra bởi BeginSend. Trước khi chuyển đổi việc triển khai thành một lambda, điều này là ok khi tôi có phương thức AsyncCallBack xử lý EndSend.

Bất kỳ trợ giúp đánh giá cao. Cảm ơn bạn trước.

Trả lời

17

Bạn chính xác rằng các thẻ lamdas không có tính đồng bộ vốn có hoặc đa luồng vốn có sẵn, nhưng Socket.BeginSend thực hiện.

Điều gì xảy ra là khối thử đóng gói cuộc gọi đến BeginSend. Nếu cuộc gọi đó thành công, không có ngoại lệ nào được ném và phương thức kèm theo trả về, bất kể điều gì xảy ra trên các luồng khác.

Nếu ngoại lệ xảy ra trong khi gọi đến BeginSend, các khối catch của bạn sẽ được gọi.

Tuy nhiên, biểu thức lambda là một cuộc gọi lại không đồng bộ, do đó, nó sẽ không được gọi cho đến sau này. Điều này xảy ra trong một callstack riêng biệt trên một thread riêng biệt, do đó, khối try không có hiệu lực ở đó.

Nếu bạn muốn xử lý lỗi cho cuộc gọi lại, bạn sẽ cần chỉ định nó bên trong chính cuộc gọi lại (có nghĩa là, bên trong lambda).

+0

Giải thích rõ ràng Mark, cảm ơn bạn .. –

7

Nó không liên quan đến lambdas. Người đại diện của cuộc gọi BeginSend thực thi trên một chuỗi khác, do đó, ngoại lệ không được ném vào chuỗi có tuyên bố catch và do đó nó không được giải quyết. Đặt xử lý ngoại lệ của bạn cùng với mã cho EndSend.

Để biết thêm thông tin, xem http://msdn.microsoft.com/en-us/library/38dxf7kt.aspx

+0

Thats những gì tôi đầu tiên mặc dù, tôi đã chỉ tự hỏi nếu tôi có thể tránh điều này :) nhưng làm cho cảm giác hoàn toàn ... –

+1

Nói rằng cuộc gọi 'BeginSend' thực hiện trên một thread không phải là loại gây hiểu lầm? –

+0

Angelo, tôi không biết bạn có ý nghĩa gì. Tất cả các phương thức Beginxxx đang thi hành trên một luồng khác với một phương thức "bắt đầu", được gọi là "", những phương thức đó đang sử dụng phần Multi I/O của CPU và tự động xử lý luồng. –

1

Các cuộc gọi đến chức năng ẩn danh được định nghĩa bởi lambda xảy ra không đồng bộ. Khối thử sẽ mất nhiều thời gian.

Bạn đang giống như: -

AsyncCallBack cb = delegate(AsyncCallback ar) { stateClient.Socket.EndSend(ar); } 
stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length, 
    SocketFlags.None, cb, stateClient); 

Bây giờ bạn có thể định nghĩa một hàm: -

void MyCallBack(AsyncCallback ar) { stateClient.Socket.EndSend(ar); } 

và sau đó là mã trên có thể trở thành: -

stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length, 
    SocketFlags.None, MyCallBack, stateClient); 

Tất cả những điều khá giống nhau trong trường hợp này. Vấn đề là Try ngoại lệ bẫy xảy ra trong quá trình thực hiện danh nghĩa của cơ thể của nó. Thực tế, mã bạn đã xác định bên trong cơ thể dưới dạng một lambda không làm cho mã đó trở nên phụ thuộc vào khối Try như là MyCallBack ở trên. Cả hai sẽ được chạy sau khi hàm có chứa khối Try hoặc có thể trong suốt nhưng trên một luồng khác.

+0

Thật vậy tôi có thể sử dụng Phương pháp ẩn danh thay vì Lambda, cảm ơn vì nỗ lực của bạn :) –

0

Như đã nêu trong các câu trả lời khác, cuộc gọi đến lambda sẽ xảy ra không đồng bộ và đó là lý do ngoại lệ không bị bắt.

Một ví dụ với đồng bộ các cuộc gọi đến đọc từ một tập tin:

File.WriteAllText("example.txt", new string('0', 2048)); 

Stream s = File.OpenRead("example.txt"); 

var buffer = new byte[1024]; 

Console.WriteLine(
    "Thread: {0} - Before asynch call...", 
    Thread.CurrentThread.ManagedThreadId); 

s.BeginRead(
    buffer, 
    0, 
    1024, 
    ar => 
    { 
     Thread.Sleep(100); // Simulate a long op 
     Console.WriteLine(
      "Thread: {0} - Callback called...", 
      Thread.CurrentThread.ManagedThreadId); 
    } 
    , 0); 

Console.WriteLine(
    "Thread: {0} - After asynch call...", 
    Thread.CurrentThread.ManagedThreadId); 

// Wait for callback to be executed 
Thread.Sleep(2000); 

Kết quả sẽ là:

Thread: 1 - Before asynch call... 
Thread: 1 - After asynch call... 
Thread: 3 - Callback called... 
0

Càng nhiều càng tốt tôi nghĩ rằng tôi là đúng cho đến bây giờ, BeginSend sẽ không bao giờ quay trở lại một ngoại lệ, tất cả các lỗi và kết quả được quay lại trên phương thức EndSend(), vì vậy tôi có thể di chuyển các khối catch try của mình.

+0

Ngoại trừ ofcourse nếu (Socket === null) sau đó BeginSend cung cấp cho bạn một NullReferenceException –

Các vấn đề liên quan