C# cpu+;的线程中止替代;时间密集型方法

C# cpu+;的线程中止替代;时间密集型方法,c#,multithreading,abort,taskfactory,thread-abort,C#,Multithreading,Abort,Taskfactory,Thread Abort,我有一个关于你的问题。我使用TaskFactory启动cpu+时间密集型方法。此方法是对SAP的调用,需要很长时间才能完成。用户应该有取消任务的选项。目前我正在使用thread.Abort(),但我知道此方法不是取消它的最佳解决方案。有没有人推荐替代方案 代码示例: Form_LoadAction loadbox = new Form_LoadAction(); Thread threadsapquery = null; Task.Factory.StartNew<>(() =&g

我有一个关于你的问题。我使用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();
}
Form_LoadAction loadbox=new Form_LoadAction();
线程threadsapquery=null;
Task.Factory.StartNew(()=>
{ 
t=线程。当前线程;
Thread.sleep(1000000000);//表示时间+cpu密集型方法
}
loadbox.ShowDialog();
if(loadbox.DialogResult==DialogResult.Abort)
{
t、 中止();
}

最好的选择是查看该方法是否支持任何类型的操作

但是,如果这不可能,取消长时间运行的进程的下一个最佳选择是使用第二个可执行文件来运行长时间运行的进程,然后通过某种形式的IPC与第二个可执行文件通信(命名管道上的WCF对机内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
    {
    }
}
YourProgram.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();
                }
            }
        }
    }
}
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();

        }
    }
}
使用图书馆数据;
使用制度;
使用系统诊断;
使用System.ServiceModel;
使用System.Threading.Tasks;
名称空间沙盒控制台
{
班级计划
{
静态void Main(字符串[]参数)
{
var connectionName=Guid.NewGuid().ToString();
ProcessStartInfo信息=新的ProcessStartInfo(“HostApp”,connectionName);
info.RedirectStandardOutput=true;
info.UseShellExecute=false;
var proxyApp=Process.Start(信息);
//阻塞,直到打印“服务已启动并准备好使用”。
proxyApp.StandardOutput.ReadLine();
var sapProxyFactory=newchannelfactory(新的NetNamedPipeBinding(),新的EndpointAddress(“net。pipe://localhost/“+连接名”);
Task.Factory.StartNew(()=>
{
var sapProxy=sapProxyFactory.CreateChannel();
尝试
{
var result=saproxy.QueryData(“一些查询”);
//对结果做点什么;
}
最后
{
sapProxy.Close();
}
});
控制台。写入线(“就绪”);
//如果在库中暂停5秒之前按enter键,则会强制“取消”该操作,从而终止宿主进程。
Console.ReadLine();
proxyApp.Kill();
Console.ReadLine();
}
}
}
我无法完全消除的一个bug是,如果你“快速失败”客户端应用程序(如在visual studio中单击停止图标),它就永远没有机会告诉宿主应用程序关闭。

你想要使用的是一个可以让你取消任务而不必像现在这样明显地干扰线程

因此,您可以将调用修改为
StartNew
,如下所示:

var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
var task = Task.Factory.StartNew<>(() => //your logic
     , ts.Token);
正如评论中所提到的,除非您处于循环中(鉴于您对CPU密集型任务的描述,这种情况可能不太可能发生),否则在完成SAP任务后,您将很难进行清理,但很可能不会比当前的实现更糟

这里有一个关于取消任务的好方法

编辑

感谢Servy指出这种方法不适用于OP的场景,因为他处理的是一个单一的、长期运行的方法,并且无法检查令牌的状态。我将保留它,但同样,这对OP不起作用。

SAP是否提供了取消任务的方法?我不知道,但这太复杂了,因为我使用了exte用于在SAP中调用查询的rnal组件。@通过ERPConnect进行的SAP函数调用是同步运行的,并且在执行时似乎没有可以取消的异步版本。当SAP互操作代码运行时,CLR极不可能中止线程。但它会顽强地继续尝试直到它返回。这与您在自己的代码中进行取消测试没有任何区别。鉴于这种情况,工作似乎不可能检查
CancellationToken
,因为只有一个方法被调用。
tokenSource2.Cancel();

try
{
    task.Wait();
}
catch(AggregateException aex)
{
    //handle TaskCanceledException here
}