【问题标题】:Cancel running task started with ThreadPool.QueueUserWorkItem取消使用 ThreadPool.QueueUserWorkItem 启动的正在运行的任务
【发布时间】:2013-05-07 13:24:13
【问题描述】:

我们有一个进程通过 ThreadPool.QueueUserWorkItem 命令启动长时间运行的任务。

里面的每个任务,通过 AppDomain.CreateDomain 调用加载一个新的 AppDomain,处理一堆东西并退出。有时,这些应用程序内部的处理时间过长,如果运行时间过长就需要中止。

我想了解是否可以在他们不知情的情况下从外部中止这些线程,以便正确卸载它们的 AppDomain?

正如您在下面的代码中看到的,目前 appdomain 是作为局部变量创建的,但如果需要,它可以向上移动到类成员。我杀死线程的最佳方法是卸载该 appdomain,就像在整个 try/catch 的“finally”子句中所做的那样。但是,我不确定我是否能够“从外部”访问它(appdomain),因为 AsyncExecute 在与中止调用者不同的线程上运行。

这是开始处理任务的函数:

public void AsyncExecute(RunningState runningState)
{
  RunningState returnedState = null;
        AppDomain runningApp = null;

        try
        {
            {

                var ads = new AppDomainSetup
                              {
                                  ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                                  DisallowBindingRedirects = false,
                                  DisallowCodeDownload = false,
                                  ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
                              };
                runningApp = AppDomain.CreateDomain("RemoteMonitor_" + runningState.LocalAccountInfo.Id, null,
                                                    ads);
                var sub =
                    runningApp.CreateInstanceAndUnwrap("Processor", "Processor.ProcessorSub")
                    as ProcessorSub;
                if (sub != null)
                {
                    returnedState = sub.Run(runningState);
                }
                else
                    Logger.Instance.WriteCritical<ExecutableItem>("Cannot create Processor object");
            }
        }
        catch (Exception ex)
        {
            Logger.Instance.WriteError<ExecutableItem>(                        ex.Message, ex);
        }
        finally
        {
            if (runningApp != null)
                AppDomain.Unload(runningApp);

            if (_onCompleteHandler == null)
                Logger.Instance.WriteCritical<ExecutableItem>("Cannot complete task");
            else
                _onCompleteHandler.Invoke(returnedState);
        }
}

【问题讨论】:

  • 你有办法在调用Run 之后中止sub 对象吗?
  • 否定。但是,我可以调用它: AppDomain.Unload(runningApp) 来卸载整个 appdomain 对象。这是我的偏好
  • 这很容易导致数据丢失并可能使锁处于打开状态...
  • 了解,但不担心锁或数据丢失。只是不确定如何从线程的“外部”调用该方法
  • 您必须有某种方式与应用程序域进行通信(通过由 mashalbyref 基础自动处理的远程处理)您要中止使用 QueueUserWorkItem 创建的任何跟踪线程(是的,您会也必须跟踪它们)。否则你真的只有卸载appdomain的能力。如果只是卸载 appdomain 不能完全正常工作,你可能会不走运。为什么不向Processor.ProcessorSub 添加一个Abort 方法?

标签: .net multithreading threadpool


【解决方案1】:

一个基本点,如果我在教如何吸鸡蛋,请提前道歉。提交到线程池的任务不一定会立即运行。他们等到池中的线程之一空闲,然后开始。

因此,如果在您调用 QueueUserWorkItem 时已经有一些线程池任务正在运行,那么您的新任务必须等待。您可以通过在调用 QueueUserWorkItem() 时记录以及在到达 try 块的第一行时再次记录来判断是否是这种情况。这两个日志条目之间的巨大时间差异暴露了原因。

可以增加线程池中的线程数。

中止被阻止的事情在很多语言中都很难干净地完成。实际上,您必须这样做的唯一原因是因为您使用的是线程池线程。但是,如果它是一个适当的线程,那么如果任务花费很长时间,那一点也不重要。假设延迟减少到等待域控制器响应,线程很可能卡在阻塞的套接字读取中。如果是这样,它不会消耗任何处理器时间,并且让它挂起多长时间自然完成或自行放弃也没有多大害处。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-10
    • 1970-01-01
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    相关资源
    最近更新 更多