c# - 如何构造一个 try-catch-finally block 来处理 finally 内部的错误?

标签 c# multithreading dll error-handling unmanaged

我在调用第三方 C++ dll 时遇到问题,我使用 DllImport 将其封装在一个类中访问其功能。

dll 要求在使用之前打开一个 session ,它返回一个整数句柄,用于在执行操作时引用该 session 。完成后,必须使用相同的句柄关闭 session 。所以我做了这样的事情:

public void DoWork(string input)
{
    int apiHandle = DllWrapper.StartSession();

    try
    {
        // do work using the apiHandle
    }
    catch(ApplicationException ex)
    {
        // log the error
    }
    finally
    {
        DllWrapper.CloseSession(apiHandle);
    }
}

我遇到的问题是 CloseSession() 有时会导致有问题的 Dll 在运行线程时抛出错误:

System.AggregateException: One or more errors occurred. ---> System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.



我不确定我能做些什么来阻止这个错误,因为它似乎是由以线程方式使用 Dll 引起的——它应该是线程安全的。但是由于我的 CloseSession() 函数除了调用 Dll 的 close 函数之外什么都不做,所以我没有太多的回旋余地来“修复”任何东西。

然而,最终结果是 session 没有正确关闭。因此,当该进程再次尝试时,它应该这样做,它会遇到一个打开的 session 并不断抛出新的错误。该 session 绝对必须关闭。

我不知道如何设计一个更强大的错误处理语句,以确保 session 始终关闭?

最佳答案

我会更改包装器以包括处理外部资源并包装句柄。 IE。不是用句柄来表示 session ,而是用包装器对象来表示它。

此外,将对 DLL 的调用封装在 lock 中。 -statements (正如@Serge 建议的那样),可以完全防止多线程问题。请注意,锁定对象是静态的,因此所有 DllWrapper 都使用相同的锁定对象。

public class DllWrapper : IDisposable
{
     private static object _lockObject = new object();

     private int _apiHandle;
     private bool _isOpen;

     public void StartSession()
     {
         lock (_lockObject) {
             _apiHandle = ...; // TODO: open the session
         }
         _isOpen = true;
     }

     public void CloseSession()
     {
         const int MaxTries = 10;

         for (int i = 0; _isOpen && i < MaxTries; i++) {
             try {
                 lock (_lockObject) {
                     // TODO: close the session
                 }
                 _isOpen = false;
             } catch {
             }
         }
     }

     public void Dispose()
     {
         CloseSession();
     }
}

请注意,这些方法现在是实例方法。

现在您可以使用 using 语句确保 session 关闭:
using (var session = new DllWrapper()) {
    try {
        session.StartSession();
        // TODO: work with the session
    } catch(ApplicationException ex) {
        // TODO: log the error
        // This is for exceptions not related to closing the session. If such exceptions
        // cannot occur, you can drop the try-catch completely.
    }       
} // Closes the session automatically by calling `Dispose()`.

你可以通过调用这个类来改进命名Session和方法OpenClose .这个类的用户不需要知道它是一个包装器。这只是一个实现细节。此外,方法的命名现在是对称的,无需重复名称 Session .

通过封装所有与 session 相关的内容,包括错误处理、从错误情况中恢复和资源处理,您可以大大减少代码中的困惑。 Session类现在是一个高级抽象。老DllWrapper处于低级和高级之间的中间距离。

关于c# - 如何构造一个 try-catch-finally block 来处理 finally 内部的错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43474225/

相关文章:

c# - 间歇性 401-SSRS 未经授权的响应

c# - 在 C# 中使用非常大的数据流像 erlang 一样传递消息

c# - 如何将 Rijndael 加密与 .Net Core 类库一起使用? (不是 .Net 框架)

java - 在决定使用 volatile 时,实际的锁是否重要?

用于 SQL 更新语句的 Java 单工作线程

c# - 无法使用 c++/cli 包装器从 c# dll 加载函数

c# - 从 WPF 应用程序中的代码访问 UserControl

c++ - 一起使用 boost::bind、boost::shared_ptr 和 boost::thread 时不调用复制构造函数

delphi - 从内存单元加载 DLL - 如何使用?

c++ - 在内存中分配多个结构