【问题标题】:stop/dispose/cancel a Task C#停止/处置/取消任务 C#
【发布时间】:2015-08-01 12:36:26
【问题描述】:

我正在尝试通过从各种存储库(如 google drive/dropbox/ftp 等)导入 DLL 来在 Windows 服务中部署 DLL...

但在实例化任何新的 DLL 之前,我希望关闭之前正在运行的实例。

我在此使用任务和反思。

我无法弄清楚如何取消在运行时实例化 DLL 的任务(因为实例化的 dll 是一个长时间运行的应用程序示例文件观察器......)

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;

            // instantiate the dll though reflection
        t = Task.Factory.StartNew(() =>
        {
            try
            {
                Assembly assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName);
                Type type = assembly.GetType("myclass.Program");

                MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic |
                          BindingFlags.Static | BindingFlags.Instance);
                minfo.Invoke(Activator.CreateInstance(type), null);

            }
            catch (Exception ex)
            {

                log.Error(ex.ToString());
            }
        }, cts.Token);

问题:我想在我的应用程序检测到新的 dll 并尝试通过此任务代码执行它之前取消任务 t。

编辑 我删除了取消令牌代码,因为它正在破坏。这是带有取消令牌的实际代码。

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;

if (t != null)
        {
            cts.Cancel();
            try
            {
                ct.ThrowIfCancellationRequested();
            }
            catch (Exception ex)
            {

                cts.Dispose();
                t.Dispose();
            }

        }

                // instantiate the dll though reflection
        t = Task.Factory.StartNew(() =>
        {
            try
            {
                Assembly assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName);
                Type type = assembly.GetType("myclass.Program");

                MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic |
                          BindingFlags.Static | BindingFlags.Instance);
                minfo.Invoke(Activator.CreateInstance(type), null);

            }
            catch (Exception ex)
            {

                log.Error(ex.ToString());
            }
        }, cts.Token);

我的想法是,如果我能以某种方式取消并处置持有实例化上下文的任务,则程序集将被释放,然后我将能够更新程序集并再次通过任务重新实例化它。

我知道我哪里出错了,请解释一下。

编辑

我对 assemblyDomain.DoCallBack(delegate) 寄予厚望。但我得到一个错误。这是引发错误的代码的简化版本。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Reflection;


namespace AppDomain
{
    [Serializable]
    class Program
    {
        static System.AppDomain assemblyDomain = null;

        static void Main(string[] args)
        {

            var inp = "go";

            while (inp.ToString().ToLower().Trim() != "stop")
            {
                start();
                inp = Console.ReadLine();
            }

        }

        private static void start()
        {


            //Check if appdomain and assembly is already loaded
            if (assemblyDomain != null)
            {
                //unload appDomain and hence the assembly
                System.AppDomain.Unload(assemblyDomain);

                //Code to download new dll
            }

            string cwd = System.AppDomain.CurrentDomain.BaseDirectory;

            string sourceFileName = @"C:\Users\guest\Documents\visual studio 2010\Projects\DotNetTraining\Lecture 1 - dotNetProgramExecution\bin\Debug\Lecture 1 - dotNetProgramExecution.exe";

            string dllName = "Lecture 1 - dotNetProgramExecution.exe";

            // copy the file
            if (File.Exists(cwd + dllName))
            {
                File.Delete(cwd + dllName);
            }

            File.Copy(sourceFileName, cwd + dllName);

            assemblyDomain = System.AppDomain.CreateDomain("assembly1Domain", null);
            assemblyDomain.DoCallBack(() =>
               {
                   var t = Task.Factory.StartNew(() =>
                   {
                       try
                       {

                           string sss = "";
                           Assembly assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName);
                           Type type = assembly.GetType("Lecture_1___dotNetProgramExecution.Program");

                           MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic |
                                     BindingFlags.Static | BindingFlags.Instance);
                           minfo.Invoke(Activator.CreateInstance(type), null);



                           //        //var pathToDll = @"assembly path";
                           //        //var dllName = "assembly name";
                           //        var assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName);
                           //        var targetAssembly = assembly.CreateInstance("Lecture_1___dotNetProgramExecution.Program");
                           //        Type type = targetAssembly.GetType();
                           //        MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
                           //        minfo.Invoke(targetAssembly, null);

                       }
                       catch (Exception ex)
                       {

                           Console.WriteLine(ex.ToString());
                       }
                   });
               });
        }
    }
}


错误 :
程序集“AppDomain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中的类型“AppDomain.Program+c__DisplayClass2”未标记为可序列化。

堆栈跟踪:

at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at AppDomain.Program.start() in c:\users\guest\documents\visual studio 2010\Projects\DotNetTraining\AppDomain\Program.cs:line 58
   at AppDomain.Program.Main(String[] args) in c:\users\guest\documents\visual studio 2010\Projects\DotNetTraining\AppDomain\Program.cs:line 24
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

请注意:我已将我正在导入的程序集中的类 Program 标记为可序列化

namespace Lecture_1___dotNetProgramExecution
{
    [Serializable]
    class Program
    {
        static void Main()
        {

更新:

动态拉取程序集的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;

namespace Lecture_1___dotNetProgramExecution
{
    [Serializable]
    class Program
    {
        static void Main()
        {
            try
            {

                Task.Factory.StartNew(() =>
                {

                    while (true)
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("log something new yippe ");
                        // flush every 20 seconds as you do it
                        File.AppendAllText(@"C:\logs.txt", sb.ToString());
                        sb.Clear();
                        Thread.Sleep(3000);
                    }

                });



                FileSystemWatcher fsw = new FileSystemWatcher();

                fsw.Path = @"c:\watched";
                //fsw.filter = ".dll";
                fsw.Created += new FileSystemEventHandler(fsw_Created);
                fsw.BeginInit();
                //throw new FileNotFoundException();
                Console.ReadLine();

            }
            catch (Exception ex)
            {

                Task.Factory.StartNew(() =>
                {

                    while (true)
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("loggind froom exception log something");
                        // flush every 20 seconds as you do it
                        File.AppendAllText(@"C:\logs.txt", sb.ToString());
                        sb.Clear();
                        Thread.Sleep(1000);
                    }

                });
                Console.ReadLine();
            }
        }

        static void fsw_Created(object sender, FileSystemEventArgs e)
        {
            throw new NotImplementedException();
        }


    }
}

【问题讨论】:

  • 哪个是慢操作Assembly.LoadFile
  • 如果您尝试部署更新的程序集,我认为您需要将程序集加载到单独的 AppDomain 中以使它们能够卸载。您基本上关闭了AppDomain 以卸载程序集并创建一个新程序集以加载新程序集。
  • 你为什么不打电话给cts.Cancel()
  • cts.cancel 不起作用。
  • @Enigmativity:我不想那样做。我想保留在我的应用程序的上下文中并删除旧的任务->然后删除旧的程序集->然后引入较新的程序集(通过下载/复制到我的本地 bin)->再次触发任务代码所以现在新的程序集被实例化,旧的上下文被销毁。

标签: c# .net task-parallel-library task cancellationtokensource


【解决方案1】:

从您的问题看来,如果有任何升级可用,您希望卸载动态加载的程序集,然后重新加载最新的程序集。在这种情况下,取消将无济于事。事实上,我没有看到您在任何地方使用取消令牌。

卸载动态加载的程序集的唯一方法是先将程序集加载到单独的应用程序域中,然后在不再需要程序集时卸载应用程序域本身。所以你应该这样做:

  1. 创建一个新的应用程序域。保留应用程序域的引用,稍后您将需要它来卸载域和程序集。
  2. 在新创建的应用程序域中加载程序集。
  3. 根据需要从新加载的程序集中创建类型实例并执行其方法。
  4. 当有新版本的 dll 可用时,卸载之前创建的应用程序域。这也会自动卸载程序集。
  5. 下载新程序集并再次从第 1 步开始。

请参阅此处了解如何在其中加载/卸载应用程序域和程序集:Using AppDomain in C# to dynamically load and unload dll

编辑:下面是带有 AppDomain.DoCallback 的代码 sn-p

using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;


namespace AppDomain
{
[Serializable]
class Program
{
    static System.AppDomain assemblyDomain = null;

    static void Main(string[] args)
    {

        var inp = "go";

        while (inp.ToString().ToLower().Trim() != "stop")
        {
            start();
            inp = Console.ReadLine();
        }

    }

    private static void start()
    {


        //Check if appdomain and assembly is already loaded
        if (assemblyDomain != null)
        {
            //unload appDomain and hence the assembly
            System.AppDomain.Unload(assemblyDomain);

            //Code to download new dll
        }

        string cwd = System.AppDomain.CurrentDomain.BaseDirectory;

        string sourceFileName = @"C:\Users\deepak\Documents\visual studio 2010\Projects\ConsoleApplication1\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe";


        string dllName = "ConsoleApplication2.exe";

        // copy the file
        if (File.Exists(cwd + dllName))
        {
            File.Delete(cwd + dllName);
        }

        File.Copy(sourceFileName, cwd + dllName);

        assemblyDomain = System.AppDomain.CreateDomain("assembly1Domain", null);
        assemblyDomain.DoCallBack(() =>
        {
            var t = Task.Factory.StartNew(() =>
            {
                try
                {

                    string sss = "";
                    string dllName1 = "ConsoleApplication2.exe";
                    Assembly assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName1);
                    Type type = assembly.GetType("Lecture_1___dotNetProgramExecution.Program");

                    MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic |
                              BindingFlags.Static | BindingFlags.Instance);
                    minfo.Invoke(Activator.CreateInstance(type), null);
                }
                catch (Exception ex)
                {

                    Console.WriteLine(ex.ToString());
                }
            });
        });
    }
}
}


using System;
using System.Text;
using System.Threading;

namespace Lecture_1___dotNetProgramExecution
{
[Serializable]
class Program
{
    static void Main()
    {
        while (true)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("log something new yippe ");
            // flush every 20 seconds as you do it
            //File.AppendAllText(@"C:\logs.txt", sb.ToString());
            Console.WriteLine(sb.ToString());
            sb.Clear();
            Thread.Sleep(3000);
        }
    }
}
}

【讨论】:

  • 我明白你想说什么,但我认为我的理解有点模糊,现在仍然如此。我已经更新了这个问题。请看一看。我将查看应用程序域链接
  • 您根本不需要取消令牌。一旦我可以访问我的笔记本电脑,我将使用代码编辑我的帖子。
  • 在阅读了将组件完全加载到单独的应用程序域的功能后,我做了完全相同的事情。但是当几分钟后我上面发现有一个更新的版本可用...上面的代码再次被触发->我成功卸载了应用程序域->然后当我尝试从我当前的 bin 中删除旧的 dll 时,它抛出一个错误说程序集是“访问路径'mydll.exe'被拒绝”
  • 我能够解决这个问题。程序集在单独的域中加载良好,但类型实例是由在当前域上下文中运行的代码创建的,并且也在当前域中加载程序集。 AppDomain.DoCallBack 成功了。请参阅我上面帖子中的更新代码。
  • 我已经为动态导入的 dll 添加了代码。请看一看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-28
  • 1970-01-01
  • 2012-06-06
  • 1970-01-01
  • 2014-11-04
  • 1970-01-01
相关资源
最近更新 更多