2013-05-11 41 views
5

Tôi có một chương trình C# hoạt động như chương trình máy khách và nhiều chương trình khách, một ứng dụng C# windows kết nối với chương trình máy chủ C# này để đọc dữ liệu từ cơ sở dữ liệu sqlite. Để tránh các vấn đề về khóa khi kết nối nhiều khách hàng tôi đã sử dụng bên dưới mã,return return before finally

System.Threading.Monitor.Enter(Lock); 

try      
{      
    filter.Execute();//get data from database 
    Request.Clear(); 
    return filter.XML;//create xml and return to client 
} 
finally 
{ 
    System.Threading.Monitor.Exit(Lock); 
} 

máy chủ bị treo một số lần và cần phải khởi động lại chương trình máy chủ. Có thực sự tốt để đưa ra tuyên bố trả về trước khi cuối cùng?

Trân Sangeetha

+1

Đây chỉ là cách khó để viết câu lệnh ** khóa **. Nó là nếu không vô ích để đảm bảo rằng chỉ có một quá trình có thể sử dụng SqlLite, bạn sẽ cần phải sử dụng một Mutex được đặt tên thay thế. –

Trả lời

4

Từ MSDN

Bằng cách sử dụng khối cuối cùng, bạn có thể dọn sạch mọi tài nguyên được cấp phát trong khối thử và bạn có thể chạy mã ngay cả khi một ngoại lệ xảy ra trong khối thử. Thông thường, các câu lệnh của một khối cuối cùng chạy khi điều khiển để lại một câu lệnh thử. Việc chuyển giao quyền kiểm soát có thể xảy ra như là kết quả của việc thực hiện bình thường, thực thi lệnh ngắt, tiếp tục, goto hoặc trả về, hoặc tuyên truyền của một ngoại lệ trong câu lệnh try.

Trong trường hợp ngoại lệ được xử lý, khối cuối cùng được liên kết được đảm bảo sẽ được chạy. Tuy nhiên, nếu ngoại lệ không được giải quyết, việc thực thi khối cuối cùng phụ thuộc vào cách hoạt động ngoại lệ được kích hoạt. Điều đó, đến lượt nó, phụ thuộc vào cách máy tính của bạn được thiết lập. Để biết thêm thông tin, hãy xem Xử lý ngoại lệ không được xử lý trong CLR.

2

Vâng, đó là những gì finally tuyên bố là cho. Nó sẽ được thực hiện sau khi return, ngay cả khi một ngoại lệ xảy ra

EDIT: mã đơn giản này sẽ cho bạn thấy, rằng khối catch là không cần thiết cho khối finally để được thực thi

public Form1() 
{ 
    InitializeComponent(); 
    check(); 
} 

private string check() 
{ 
    try 
    { 
     return String.Empty; 
    } 
    finally 
    { 
     MessageBox.Show("finally"); 
    } 
} 
+1

Cái gì? Làm như thế nào??? Bạn có bất kỳ tài liệu nào không? –

+0

@ bash.d Chạy mã OP trong trình gỡ lỗi và bạn sẽ thấy :) – VladL

+0

@ bash.d đó là hành vi được xác định. Bạn đã đọc những gì bạn đã đăng? : D "Thông thường, các báo cáo của một khối cuối cùng chạy khi kiểm soát để lại một tuyên bố thử. Việc chuyển giao kiểm soát có thể xảy ra như là kết quả của việc thực hiện bình thường ... hoặc trả về" – VladL

1

Vì bạn không có khối catch, không có đảm bảo rằng cuối cùng sẽ được thực hiện. Từ MSDN - try-finally (C# Reference)"Locks and exceptions do not mix" (Eric Lippert)

Trong trường hợp ngoại lệ được xử lý, khối cuối cùng được liên kết được đảm bảo để chạy. Tuy nhiên, nếu ngoại lệ không được giải quyết, việc thực thi khối cuối cùng phụ thuộc vào cách hoạt động ngoại lệ được giải phóng là kích hoạt. Điều đó, đến lượt nó, phụ thuộc vào cách máy tính của bạn được thiết lập.

Và từ liên kết sau đó được đề cập (Unhandled Exception Processing In The CLR) thì có nhiều cân nhắc khác nhau có thể có nghĩa là bạn kết thúc bằng chuỗi bị chấm dứt. Tôi không thành thật biết nếu điều đó sẽ để lại cho bạn một khóa trên đối tượng khóa mặc dù.

Nếu bạn muốn đảm bảo rằng:

  • bạn phát hành các khóa; nhưng
  • bạn không muốn để xử lý các ngoại lệ ở cấp độ này, thay vào đó bạn muốn nó xử lý bởi một bộ xử lý ngoại lệ mức độ cao hơn

Sau đó làm:

TheXmlType xml = null; 
Monitor.Enter(Lock); 
bool inLock = true; 
try { 
    ... 
    xml = filter.Xml; // put this here in case it throws an exception 
    inLock = false; // set this here in case monitor.exit is to 
    // throw an exception so we don't do the same all over again in the catch block 
    Monitor.Exit(Lock); 
    return xml; // this is fine here, but i would normally put it outside my try 
} 
catch (Exception) { 
    if (inLock) Monitor.Exit(Lock); 
    throw; 
} 

Nhưng, xin lưu ý: don Không sử dụng catch (Exception) để ẩn ngoại lệ, đây chỉ là ok nếu bạn đang ném lại ngoại lệ. Mọi người cũng khuyên bạn nên có một tuyên bố return duy nhất và thường điều này sẽ nằm ngoài khối thử của bạn.

EDIT:

Xác nhận với một chương trình kiểm tra, và từ MSDN - Exceptions in Managed Threads

Bắt đầu với phiên bản .NET Framework 2.0, ngôn ngữ chung runtime cho phép hầu hết các trường hợp ngoại lệ unhandled trong chủ đề để tiến hành một cách tự nhiên. Trong hầu hết các trường hợp, điều này có nghĩa là ngoại lệ không được xử lý khiến ứng dụng chấm dứt.

Vì vậy, nếu bạn không xử lý ngoại lệ, ứng dụng của bạn sẽ bị lỗi (và bạn sẽ không phải lo lắng về khóa). Nếu bạn xử lý nó, sau đó mã ban đầu của bạn sẽ thực thi nó cuối cùng là khối và bạn là ok.

EDIT 2: Mã Kiểm tra cập nhật vì nó không đúng cách minh họa cho một tổ chức phi bắn cuối cùng:

class Program 
{ 
    static void Main(string[] args) { 
     Program p =new Program(); 
     p.Start(); 
     Console.WriteLine("done, press enter to finish"); 
     Console.ReadLine(); 
    } 

    private readonly object SyncRoot = new object(); 
    ManualResetEvent mre = new ManualResetEvent(false); 

    private void Start() { 
     /* 
     * The application will run the thread, which throws an exception 
     * While Windows kicks in to deal with it and terminate the app, we still get 
     * a couple of "Failed to lock" messages 
     * */ 

     Thread t1 = new Thread(SetLockAndTerminate); 
     t1.Start(); 
     mre.WaitOne(); 
     for (int i = 0; i < 10; i++) { 
      if (!Monitor.TryEnter(this.SyncRoot, 1000)) { 
       Console.WriteLine("Failed to lock"); 
      } 
      else { 
       Console.WriteLine("lock succeeded"); 
       return; 
      } 
     } 
     Console.WriteLine("FINALLY NOT CALLED"); 
    } 
    public int CauseAnOverflow(int i) 
    { 
     return CauseAnOverflow(i + 1); 
    } 
    public void SetLockAndTerminate() { 
     Monitor.Enter(this.SyncRoot); 
     Console.WriteLine("Entered"); 
     try { 
      mre.Set(); 
      CauseAnOverflow(1); // Cause a stack overflow, prevents finally firing 
     } 
     finally { 
      Console.WriteLine("Exiting"); 
      Monitor.Exit(this.SyncRoot); 
     } 
    } 
} 
0

Is it bad practice to return from within a try catch finally block?
Đó là cách bên phải của văn bản Xử lý ngoại lệ trong C# và luôn khối finally sẽ được thực hiện, nó không phụ thuộc vào nơi trả lại. Tôi không biết về mã của bạn nhưng bạn sẽ tìm thấy vấn đề của bạn ở một nơi khác (ví dụ nếu mã của bạn được lưu trữ trong IIS, tôi nghi ngờ trạng thái Khóa đối tượng trong các miền được tải khác nhau hoặc có thể khóa chỉ dành cho một cuộc gọi đến và xảy ra trong cơ sở dữ liệu hoặc Request.Clear là gì() bạn không có khối khóa ở đó?). Bạn có thể dễ dàng ghi lại trạng thái cuộc gọi đến và tìm ra sự cố.