5

我有充当客户端的 ac# 程序,许多客户端程序又是 ac# windows 应用程序连接到这个 c# 服务器程序以从 sqlite 数据库读取数据。为了避免连接多个客户端时出现锁定问题,我使用了以下代码,

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

服务器有时会挂起,需要重新启动服务器程序。在 finally 之前做出 return 声明是一个好习惯吗?

问候桑吉塔

4

4 回答 4

4

来自MSDN

通过使用 finally 块,您可以清理在 try 块中分配的任何资源,并且即使在 try 块中发生异常,您也可以运行代码。通常,finally 块的语句在控制离开 try 语句时运行。控制的转移可能是正常执行、break、continue、goto 或 return 语句的执行,或者异常从 try 语句中传播出来的结果。

在已处理的异常中,保证运行关联的 finally 块。但是,如果异常未处理,finally 块的执行取决于异常展开操作的触发方式。反过来,这取决于您的计算机的设置方式。有关详细信息,请参阅 CLR 中的未处理异常处理。

于 2013-05-11T10:27:10.283 回答
2

是的,这就是finally声明的用途。它会在 之后执行return,即使发生异常

编辑:这个简单的代码会告诉你,执行 finally 块不需要 catch 块

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

private string check()
{
    try
    {
        return String.Empty;
    }
    finally
    {
        MessageBox.Show("finally");
    }
}
于 2013-05-11T10:25:32.623 回答
1

由于您没有 catch 块,因此无法保证 finally 将被执行。来自MSDN - try-finally(C# 参考)“锁和异常不混合”(Eric Lippert)

在已处理的异常中,保证运行关联的 finally 块。但是,如果异常未处理,finally 块的执行取决于异常展开操作的触发方式。反过来,这取决于您的计算机的设置方式。

然后从提到的链接(CLR 中的未处理异常处理)中,有各种考虑因素可能意味着您最终会得到一个终止的线程。不过,老实说,我不知道这是否会让您锁定锁定对象。

如果您想确保:

  • 你释放锁;但
  • 您不想在此级别处理异常,而是希望它由更高级别的异常处理程序处理

然后做:

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

但是,请注意:不要catch (Exception)用于隐藏异常,只有在重新抛出异常时才可以。人们还建议你有一个return语句,通常这会在你的 try 块之外。

编辑:

通过测试程序和MSDN 确认 - 托管线程中的异常

从 .NET Framework 2.0 版开始,公共语言运行时允许线程中大多数未处理的异常自然地进行。在大多数情况下,这意味着未处理的异常会导致应用程序终止。

所以,如果你不处理你的异常,你的应用程序就会崩溃(你不必担心锁)。如果您确实处理了它,那么您的原始代码将在 finally 块中执行它,您就可以了。

编辑 2:测试代码已更新,因为它最终没有正确说明不触发:

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);
        }
    }
}
于 2013-05-11T11:08:17.010 回答
0

从 try catch finally 块中返回是不好的做法吗?
这是在 c# 中编写异常处理的正确方法,并且总是会执行 finally 块,它不依赖于返回的位置。我不知道您的代码,但您应该在其他地方找到您的问题(例如,如果您的代码托管在 IIS 中,我会怀疑 Lock 对象在不同加载域中的状态,或者锁可能只是针对一个来电而发生在数据库中或什么是 Request.Clear() 你那里没有锁块?)。您可以轻松记录来电状态并找出问题所在。

于 2013-05-11T17:12:37.937 回答