【问题标题】:Force Cancel Task with API that might hang使用可能挂起的 API 强制取消任务
【发布时间】:2015-04-19 00:54:20
【问题描述】:

我目前正在使用串行端口,即使设置了自己的超时,我使用的 API 有时也会在读取时挂起。

这不是一个大问题,但是当发生这种情况并且需要关闭挂起的线程时,我需要做一些工作。我已经尝试过以下方法,但它一直给我带来问题,因为 API 调用没有终止,但允许在其余代码继续时继续,并且抛出了 TimeoutException。如何使用Tasks 在一定时间后取消挂起的任务?

CancellationToken token = new CancellationToken();
var task = Task.Factory.StartNew(() => 
           {
               CallingAPIThatMightHang(); // Example
           }, token);

if (!task.Wait(this.TimeToTimeOut, token))
{
    throw new TimeoutException("The operation timed out");
}

【问题讨论】:

标签: c# multithreading task-parallel-library


【解决方案1】:

CancellationToken是合作取消的形式。您需要在执行操作时调节令牌并观察是否已请求取消。

从您的代码块来看,您似乎有一个长时间运行的同步操作,您将其卸载到线程池线程。如果是这种情况,请查看是否可以将该串行调用分离到块,您可以在读取块后轮询令牌。如果您不能,则无法取消。

请注意,为了请求取消,您必须创建一个 CancellationTokenSource,稍后您可以将其称为 Cancel() 方法。

附带说明一下,串口是异步 IO,您可以自然地使用异步 API,而不是将同步卸载到线程池线程。

编辑:

@HansPassant 给出了一个更好的主意。在另一个进程中运行第三方调用,你保留一个引用。一旦您需要终止它,请终止该进程。

例如:

void Main()
{
    SomeMethodThatDoesStuff();
}

void SomeMethodThatDoesStuff()
{
   // Do actual stuff
}

然后在单独的进程中启动它:

private Process processThatDoesStuff;

void Main()
{
    processThatDoesStuff = Process.Start(@"SomeLocation");
    // Do your checks here.

    if (someCondition == null)
    {
        processThatDoesStuff.Kill();
    }
}

如果您需要在这两个进程之间传递任何结果,您可以通过多种机制来完成。一种是写入和读取该过程的标准输出。

【讨论】:

  • 很遗憾这是不可能的。
  • 看来敌对api不是OP写的。
  • 那么取消是不可能的:/
  • 我的comment here 也差不多:)
  • @downvoter 我很想解释一下那个downvote。
【解决方案2】:

很遗憾,我无法使用任何其他框架,也无法更改我正在调用的 API,以便它可以使用取消令牌。

这就是我选择解决问题的方式。

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var result = TestThreadTimeOut();
            Console.WriteLine("Result: " + result);
        }
        catch (TimeoutException exp)
        {
            Console.WriteLine("Time out");
        }
        catch (Exception exp)
        {
            Console.WriteLine("Other error! " + exp.Message);
        }

        Console.WriteLine("Done!");
        Console.ReadLine();
    }

    public static string TestThreadTimeOut()
    {
        string result = null;
        Thread t = new Thread(() =>
        {
            while (true)
            {
                Console.WriteLine("Blah Blah Blah");
            }
        });
        t.Start();
        DateTime end = DateTime.Now + new TimeSpan(0, 0, 0, 0, 1500);
        while (DateTime.Now <= end)
        {
            if (result != null)
            {
                break;
            }
            Thread.Sleep(50);
        }
        if (result == null)
        {
            try
            {
                t.Abort();
            }
            catch (ThreadAbortException)
            {
                // Fine
            }
            throw new TimeoutException();
        }
        return result;
    }
}

【讨论】:

  • 查看我的编辑。也许这比使用Thread.Abort 更好
  • Thread.Abort() 是一把大锤,可能会使您的程序处于损坏状态。我真的真的不建议使用它。
  • 你想让我用什么代替?
  • @ScottChamberlain 我完全赞同这一点。 whileThread.Sleep 也让我眼前一亮……Timer 的使用会更好,因为不会有任何旋转……
  • @DoomStone Yuval Itzchakov 的编辑答案是正确的方法。您创建了第二个过程并在那里进行。如果您需要在两个进程之间进行通信,请使用某种形式的IPC,我发现WCF over Named Pipes 效果非常好,并且易于用于同一台机器的 IPC。
猜你喜欢
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-19
  • 2013-12-26
  • 1970-01-01
相关资源
最近更新 更多