【问题标题】:C# how cancel an executing methodC#如何取消正在执行的方法
【发布时间】:2015-06-09 04:13:59
【问题描述】:

我有一个委托方法在我的应用程序中运行繁重的进程(我必须使用 MS Framework 3.5):

private delegate void delRunJob(string strBox, string strJob);

执行:

    private void run()
    {
        string strBox = "G4P";
        string strJob = "Test";

        delRunJob delegateRunJob = new delRunJob(runJobThread);
        delegateRunJob.Invoke(strBox, strJob);
    }

在方法的某些部分runJobThread

我调用外部程序(SAP - 远程函数调用)来检索数据。该行的执行可能需要 1-30 分钟。

private void runJobThread(string strBox, string strJob)
{
    // CODE ...
    sapLocFunction.Call(); // When this line is running I cannot cancel the process
    // CODE ...
}

我想让用户取消整个过程。

如何做到这一点?我尝试了一些方法;但我也陷入了同样的境地;当这条特定线路运行时,我无法停止该过程。

【问题讨论】:

  • 您必须结合使用 async 和 await 以及取消令牌。
  • @PhilipStuyck,这会取消操作本身,还是只是取消等待,让操作成为孤立的并在后台运行?
  • 这取决于您的代码,您必须检查长时间运行方法中的取消令牌,如果您看到任务被取消,则抛出任务取消异常。
  • “你的”代码是 sapLocFunction.Call(),它不需要 CancellationToken,除非有过载。
  • @glenebob 正确。也应该修改它以将取消标记作为参数。如果这是不可能的,那么就没有办法以优雅的方式进行取消。

标签: c# multithreading delegates thread-safety


【解决方案1】:

您必须研究异步和等待机制,而不是使用委托机制。当您了解此机制后,您可以转到取消令牌。 可以在此处找到同时执行这两项操作的示例: http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx

【讨论】:

  • 这绝对是最好的答案。
  • 我不确定他的情况,但我认为这不会达到他想要的效果 - 他可以在代码等待 sapLocFunction.Call() 时调用 ct.ThrowIfCancellationRequested(); 吗?
  • 该调用应在 saplocfunction.call 方法中,并且应将 canceltoken 传递给它。该方法也可能需要重构以利用 async 和 await。
【解决方案2】:

好吧;我找到了一种复杂但有效的方法来解决我的问题:

a.) 我创建了一个“Helper 应用程序”来在进程运行时显示通知图标(以确保不干扰主应用程序的正常执行):

private void callHelper(bool blnClose = false)
{
    if (blnClose)
        fw.processKill("SDM Helper");
    else
        Process.Start(fw.appGetPath + "SDM Helper.exe");
}

b.) 我创建了一个只调用繁重进程行的线程。

c.) 当线程处于活动状态时,我会检查名为“cancel”的外部文件(“Helper 应用程序”会执行此操作;当用户单击选项以取消进程时,Helper 会创建文件)。

d.) 如果文件存在;处理所有对象并中断 while 循环。

e.) sapLocFunction.Call() 方法会引发异常,但我预计会出错。

private void runJobThread(string strBox, string strJob)
{
    // CODE ...

    Thread thrSapCall = new Thread(() =>
    {
        try { sapLocFunction.Call(); }
        catch { /* Do nothing */ }
    });

    thrSapCall.Start();

    while (thrSapCall.IsAlive)
    {
        Thread.Sleep(1000);
        try
        {
            if (fw.fileExists(fw.appGetPath + "\\cancel"))
            {
                sapLocFunction = null;
                sapLocTable = null;
                sapConn.Logoff();
                sapConn = null;
                canceled = true;
                break;
            }
        }
        finally { /* Do nothing */ }
    }

    thrSapCall = null;

    // CODE ...
}

像魅力一样工作!

【讨论】:

    【解决方案3】:

    我认为您将不得不求助于described here 方法。阅读这篇文章,了解为什么这离理想还有很长的路要走。 也许这可能有用...

    private void runJobThread(string strBox, string strJob, CancellationToken token)
    {
       Thread t = Thread.CurrentThread;
        using (token.Register(t.Abort))
        {
        // CODE ...
        sapLocFunction.Call(); // When this line is running I cannot cancel the process
        // CODE ...
       } 
    }
    

    【讨论】:

      【解决方案4】:

      dnspy 的一点点在 nco3.0 上公开了一个取消方法。

          private readonly static Type RfcConnection = typeof(RfcSessionManager).Assembly.GetType("SAP.Middleware.Connector.RfcConnection");
          
          private readonly static Func<RfcDestination, object> GetConnection = typeof(RfcSessionManager).GetMethod("GetConnection", BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(typeof(Func<RfcDestination, object>)) as Func<RfcDestination, object>;
          
          private readonly static MethodInfo Cancel = RfcConnection.GetMethod("Cancel", BindingFlags.Instance | BindingFlags.NonPublic);
      
      
          object connection = null;
          var completed = true;
          
          using (var task = Task.Run(() => { connection = GetConnection(destination); rfcFunction.Invoke(destination); }))
      {  
          try
          {
               completed = task.Wait(TimeSpan.FromSeconds(invokeTimeout));
          
               if (!completed)
                   Cancel.Invoke(connection, null);                            
                                      
               task.Wait();
          }
          catch(AggregateException e)
          {
               if (e.InnerException is RfcCommunicationCanceledException && !completed)
                   throw new TimeoutException($"SAP FM {functionName} on {destination} did not respond in {timeout} seconds.");
               throw;
          }   
      }
      

      【讨论】:

        猜你喜欢
        • 2012-11-09
        • 2011-02-13
        • 2012-09-08
        • 1970-01-01
        • 1970-01-01
        • 2019-10-04
        • 1970-01-01
        • 1970-01-01
        • 2011-03-03
        相关资源
        最近更新 更多