Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 按时间取消任务_C#_Multithreading_Task_Cancellationtokensource - Fatal编程技术网

C# 按时间取消任务

C# 按时间取消任务,c#,multithreading,task,cancellationtokensource,C#,Multithreading,Task,Cancellationtokensource,我有一个多线程应用程序,我需要在一段时间后取消每个任务,即使在取消时,它们使用非托管资源。现在我使用以下代码(例如,控制台应用程序)。在实际应用程序中,延迟可能发生在非托管资源中 static void Main() { for (int i = 0; i < 10; i++) { Task.Factory.StartNew(Do, TaskCreationOptions.LongRunning); }

我有一个多线程应用程序,我需要在一段时间后取消每个任务,即使在取消时,它们使用非托管资源。现在我使用以下代码(例如,控制台应用程序)。在实际应用程序中,延迟可能发生在非托管资源中

static void Main()
    {
        for (int i = 0; i < 10; i++)
        {
            Task.Factory.StartNew(Do, TaskCreationOptions.LongRunning);
        }

        Console.ReadLine();
    }

    private static void Do()
    {
        new Timer(Thread.CurrentThread.Abort, null, 1000, -1);

        try
        {
            Console.WriteLine("Start " + Task.CurrentId);
            Thread.Sleep(2000);
            Console.WriteLine("End " + Task.CurrentId);
        }
        catch (Exception)
        {
            Console.WriteLine("Thread Aborted " + Task.CurrentId);
        }
    }
static void Main()
{
对于(int i=0;i<10;i++)
{
Task.Factory.StartNew(Do,TaskCreationOptions.LongRunning);
}
Console.ReadLine();
}
私有静态void Do()
{
新计时器(Thread.CurrentThread.Abort,null,1000,-1);
尝试
{
Console.WriteLine(“开始”+任务.CurrentId);
《睡眠》(2000年);
控制台.WriteLine(“结束”+任务.CurrentId);
}
捕获(例外)
{
Console.WriteLine(“线程中止”+Task.CurrentId);
}
}
得到结果:

但从安全的角度来看,我不确定它是否适合实际应用。 我还在不同的变体中使用了CancellationToken,但它并没有给出正确的结果,因为我在timespan中使用CancelAfter()或.Delay(),在ceratain time之后使用cancel task,我得到了以下结果:

static void Main()
    {
        for (int i = 0; i < 10; i++)
        {
            var clt = new CancellationTokenSource();

            Task task = new Task(() =>
            {
                Task.Delay(2000).ContinueWith(_ =>
                {
                    clt.Cancel();

                }, clt.Token);

                Do(clt.Token);

            }, clt.Token);

            task.Start();
        }

        Console.ReadLine();
    }

    private static void Do(CancellationToken cltToken)
    {
        Console.WriteLine("Start " + Task.CurrentId);

        Thread.Sleep(2500);

        if (!cltToken.IsCancellationRequested)
        {
            Console.WriteLine("End " + Task.CurrentId);
        }
        else
        {
            Console.WriteLine("Cancelled "+ Task.CurrentId);
        }
    }
static void Main()
{
对于(int i=0;i<10;i++)
{
var clt=新的CancellationTokenSource();
任务=新任务(()=>
{
任务。延迟(2000)。继续(=>
{
clt.Cancel();
},clt.Token);
Do(clt.Token);
},clt.Token);
task.Start();
}
Console.ReadLine();
}
私有静态无效Do(CancellationToken cltToken)
{
Console.WriteLine(“开始”+任务.CurrentId);
睡眠(2500);
如果(!cltToken.iscancellationrequest)
{
控制台.WriteLine(“结束”+任务.CurrentId);
}
其他的
{
Console.WriteLine(“已取消”+任务.CurrentId);
}
}

在这种情况下,必须取消所有任务,因为Thread.Sleep()>分配给执行每个任务的时间不足。但我们可以看到,有些时间需要执行

我还使用了以下结构并给出了相同的结果:

        static void Main()
    {
        for (int i = 0; i < 10; i++)
        {
            var clt = new CancellationTokenSource();
            clt.CancelAfter(2000);

            Task.Factory.StartNew(Do, clt.Token);

        }

        Console.ReadLine();
    }

    private static void Do(object obj)
    {
        var cltToken = (CancellationToken) obj;

        Console.WriteLine("Start " + Task.CurrentId);

        Thread.Sleep(2500);

        if (!cltToken.IsCancellationRequested)
        {
            Console.WriteLine("End " + Task.CurrentId);
        }
        else
        {
            Console.WriteLine("Cancelled "+ Task.CurrentId);
        }
    }
static void Main()
{
对于(int i=0;i<10;i++)
{
var clt=新的CancellationTokenSource();
clt.CancelAfter(2000年);
Task.Factory.StartNew(Do,clt.Token);
}
Console.ReadLine();
}
私有静态void Do(对象对象对象)
{
var cltToken=(CancellationToken)obj;
Console.WriteLine(“开始”+任务.CurrentId);
睡眠(2500);
如果(!cltToken.iscancellationrequest)
{
控制台.WriteLine(“结束”+任务.CurrentId);
}
其他的
{
Console.WriteLine(“已取消”+任务.CurrentId);
}
}

我还在方法Do()中使用并行和初始化取消令牌,并在timespan之后使用计时器取消令牌,但都给出相同的结果


那么,为什么会发生这种情况?在一段时间后取消任务的正确方法是什么?

通过使用相同的计时,您可以获得与原始“中止”版本相同的结果。例如,此代码:

static void Main()
{
    var clt = new CancellationTokenSource();
    clt.CancelAfter(1000);
    for (int i = 0; i < 10; i++)
    {
        Task.Run(() => Do(clt.Token));
    }
    Console.ReadLine();
}

private static void Do(CancellationToken cltToken)
{
    Console.WriteLine("Start " + Task.CurrentId);
    Thread.Sleep(2000);

    if (!cltToken.IsCancellationRequested)
    {
        Console.WriteLine("End " + Task.CurrentId);
    }
    else
    {
        Console.WriteLine("Cancelled "+ Task.CurrentId);
    }
}
使用
CancellationTokenSource
比中止线程更好<代码>线程。中止是一个坏主意,因为它在没有提供正确的清理机制的情况下中止线程。使用令牌允许您以干净的方式合作处理取消


至于为什么你的其他选项不能正常工作,你使用的时间安排有点太接近了。在调试器下运行时,这尤其是一个问题,因为它会阻止计时(即:
CancelAfter
以及
Thread.Sleep
)同时触发。如果在VisualStudio主机进程之外运行发布版本,您可能会发现它们的工作更加可靠

首先,您看到的取消令牌未发出信号的问题可能是由于细微的时间变化
CancelAfter
应该可以正常工作,但您需要增加超时和睡眠之间的差异,以便更真实地了解将发生的情况

第二,我可能是坏消息的预兆,但是如果这个非托管资源没有提供优雅地终止操作的机制,那么这就变得非常困难。原因是:

  • 显然,当线程正在执行非托管代码时,无法轮询
    CancellationToken
    。因此,没有办法启动一个优雅的关闭自己
  • 除了您无论如何都不应该中止线程之外,
    thread.Abort
    调用不会将中止信号注入目标,直到它重新加入托管域。换句话说,中止不会终止正在执行非托管代码的线程。这样做是为了使中止更安全
唯一可靠地实现这一点的方法是在进程外运行非托管资源。这意味着您需要启动一个新进程来执行非托管代码,然后使用WCF(或其他通信协议)来回发送消息/数据。如果非托管资源没有及时响应,则可以终止进程。这是安全的,因为杀死另一个进程不会破坏当前进程的状态

希望您正在使用的任何非托管资源都有一个内置了优雅终止机制的API。如果它写得好,它可能有这样一个功能,但我的经验表明,许多人没有

Start 111
Start 112
Start 113
Start 114
Start 115
Start 116
Start 117
Start 118
Start 119
Start 120
Cancelled 111
Cancelled 112
Cancelled 118
Cancelled 116
Cancelled 114
Cancelled 113
Cancelled 117
Cancelled 115
Cancelled 119
Cancelled 120