Thread.Abort的替代方案,用于cpu +时间密集型方法

Mar*_*elD 3 c# multithreading abort thread-abort taskfactory

我有一个关于多线程应用程序的问题.我使用TaskFactory来启动一个cpu +时间密集型方法.此方法是对SAP的调用,需要很长时间才能完成.用户应该有一个取消任务的选项.目前我正在使用thread.Abort(),但我知道这种方法不是取消它的最佳解决方案.有没有人有替代方案的建议?

代码示例:

Form_LoadAction loadbox = new Form_LoadAction();
Thread threadsapquery = null;

Task.Factory.StartNew<>(() => 
{ 
   t = Thread.CurrentThread;
   Thread.sleep(10000000000); //represents time + cpu intensive method
}

loadbox.ShowDialog();
if (loadbox.DialogResult == DialogResult.Abort)
{
   t.Abort();
}
Run Code Online (Sandbox Code Playgroud)

Sco*_*ain 5

最好的选择是查看该方法是否支持任何类型的合作取消.

但是,如果不可能,则取消长时间运行的进程的下一个最佳选择是使用运行长时间运行进程的第二个可执行文件,然后通过某种形式的IPC与第二个可执行文件进行通信(WCF over Named Pipes适用于内部-machine IPC)"代理"所有呼叫.当您需要取消您的进程时,您可以终止第二个代理exe并且所有句柄都将被正确释放(在哪里Thread.Abort()不会).

这是一个完整的例子.有3个文件,一个在两个可执行文件之间共享的公共库,它包含代理的接口和实现,托管应用程序和客户端应用程序.托管应用程序和公共库可能会合并到一个程序集中.

LibraryData.dll

//ISapProxy.cs
using System.Collections.Generic;
using System.ServiceModel;

namespace LibraryData
{
    [ServiceContract]
    public interface ISapProxy
    {
        [OperationContract]
        List<SapData> QueryData(string query);

        [OperationContract]
        void Close();
    }
}


//SapProxy.cs
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;

namespace LibraryData
{
    public class SapProxy : ISapProxy
    {
        public List<SapData> QueryData(string query)
        {
            Thread.Sleep(new TimeSpan(0, 0, 5)); //represents time + cpu intensive method

            return new List<SapData>();
        }


        public void Close()
        {
            Application.Exit();
        }
    }
}


//SapData.cs
using System.Runtime.Serialization;

namespace LibraryData
{
    [DataContract]
    public class SapData
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

HostApp.exe

//Program.cs
using LibraryData;
using System;
using System.ServiceModel;
using System.Windows.Forms;

namespace HostApp
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            System.Diagnostics.Debugger.Launch();
            if (args.Length > 0)
            {
                var uri = new Uri("net.pipe://localhost");
                using (var host = new ServiceHost(typeof(SapProxy), uri))
                {
                    //If a client connection fails, shutdown.
                    host.Faulted += (obj, arg) => Application.Exit();

                    host.AddServiceEndpoint(typeof(ISapProxy), new NetNamedPipeBinding(), args[0]);
                    host.Open();
                    Console.WriteLine("Service has started and is ready to use.");

                    //Start a message loop in the event the service proxy needs one.
                    Application.Run();

                    host.Close();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

YourProgram.exe

using LibraryData;
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading.Tasks;

namespace SandboxConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            var connectionName = Guid.NewGuid().ToString();
            ProcessStartInfo info = new ProcessStartInfo("HostApp", connectionName);
            info.RedirectStandardOutput = true;
            info.UseShellExecute = false;

            var proxyApp = Process.Start(info);

            //Blocks till "Service has started and is ready to use." is printed.
            proxyApp.StandardOutput.ReadLine();

            var sapProxyFactory = new ChannelFactory<ISapProxy>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/" + connectionName));
            Task.Factory.StartNew(() =>
            {
                var sapProxy = sapProxyFactory.CreateChannel();

                try
                {
                    var result = sapProxy.QueryData("Some query");

                    //Do somthing with the result;
                }
                finally
                {
                    sapProxy.Close();
                }
            });

            Console.WriteLine("ready");

            //If you hit enter here before the 5 second pause in the library is done it will kill the hosting process forcefully "canceling" the operation.
            Console.ReadLine();

            proxyApp.Kill();

            Console.ReadLine();

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我无法完全压制的一个错误是,如果您"快速失败"客户端应用程序(例如通过单击visual studio中的停止图标),它永远不会有机会告诉托管应用程序关闭.