C#超时 - 是我的危险......?

时间:2012-09-07 09:27:17

标签: c# thread-safety timeout invoke

我已根据我在各个地方看到过的东西创建了一个超时功能,但我很确定我没有这么做! (但它确实有效。)

我正在连接到一块硬件,如果工作连接几秒钟,但如果不工作需要大约1分钟才能超时。因此,如果我可以创建自己的超时功能,我可以将其设置为20秒并节省大量时间并等待。

我试过这样做,所以我的超时返回一个字符串:

static string CallWithTimeout(Action action, int timeoutMilliseconds)
{
    string reply = "";
    Thread threadToKill = null;
    Action wrappedAction = () =>
    {
        threadToKill = Thread.CurrentThread;
        action();
    };

    IAsyncResult result = wrappedAction.BeginInvoke(null, null);
    if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds))
    {
        reply = "Connected";
        wrappedAction.EndInvoke(result);
        return reply;
    }
    else
    {
        threadToKill.Abort();
        reply = "Error";
        return reply;            
    }      
}

然后用类似的东西来称呼它:

string replyfromreader = CallWithTimeout(connectToHardware, 20000);

connectToHardware只是一个班轮,因此无需发帖。

2 个答案:

答案 0 :(得分:1)

就.NET状态而言,这是可以接受的。您不会调用EndInvoke(),它会将资源泄漏10分钟,这是远程对象的默认生命周期。

在这种情况下,调用Thread.Abort()的非常成功的可能性很小。托管线程需要处于可警告的等待状态才能中止,它永远不会是线程深埋在本机代码中最终等待某些设备驱动程序调用完成的时候。

让CLR处于不断尝试中止线程并且永远不会成功的状态并不是特别令人愉快,这不是我曾经故意尝试的事情,所以不知道副作用是什么。但它确实意味着您的代码将阻止Abort()方法调用,因此您仍然没有解决问题。因此,最好的办法是中止线程但只是放弃它。设置标记设备已死的标志,这样您就不会再尝试这样做了。

如果您想继续运行程序,即使设备没有处于可用状态,并且您想提供一种从问题中恢复的方法,那么您将需要一种完全不同的方法。您需要将设备相关代码放在单独的进程中。然后您可以在设备无响应时杀死(),依靠Windows来清理弹片。使用像命名管道或套接字这样的低级机制来处理该进程是最好的,这样您就可以非常轻松地从断开连接中恢复。

答案 1 :(得分:0)

避免Thread.Abort总是一个好主意。在你没有创建的线程上避免它更好。

假设硬件不工作,并且你想要超时,那么connectToHardware是否自己超时并且不需要错误/异常细节也没关系,那么你可以使用任务并行图书馆(TPL):System.Threading.Tasks.Task

// True => worked, False => timeout
public static bool CallWithTimeout(Action method, TimeSpan timeout) {
  Exception e;
  Task worker = Task.Factory.StartNew(method)
                    .ContineueWith(t => {
                      // Ensure any exception is observed, is no-op if no exception.
                      // Using closure to help avoid this being optimised out.
                      e = t.Exception;
                    });
  return worker.Wait(timeout);
}

(如果传递的Action可以与传递的CancellationToken进行交互,则可以使其变得更干净,从而允许基础方法在超时时快速失败。)