Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/18.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# 从TaskScheduler中取消TPL任务_C#_.net_Task Parallel Library - Fatal编程技术网

C# 从TaskScheduler中取消TPL任务

C# 从TaskScheduler中取消TPL任务,c#,.net,task-parallel-library,C#,.net,Task Parallel Library,我正在尝试创建一个TaskScheduler,它按顺序运行所有任务,但只会“完成”最近计划的任务。例如,如果我使用它来计划任务A,那么在它完成计划任务B和C之前,我只希望C被认为是成功的。A可以继续其工作,但在完成时应被视为“取消”,而B应在开始之前标记为“取消” 我已经有了在线程池上按顺序执行委托的现有代码,并且管理最多2个排队任务的想法——一个当前正在执行,另一个是下一个。缺少的部分能够将任务的结果状态设置为“已取消” 不幸的是,在任务调度器中,您实际上几乎无法访问任务的状态或任何取消令牌

我正在尝试创建一个
TaskScheduler
,它按顺序运行所有任务,但只会“完成”最近计划的任务。例如,如果我使用它来计划任务A,那么在它完成计划任务B和C之前,我只希望C被认为是成功的。A可以继续其工作,但在完成时应被视为“取消”,而B应在开始之前标记为“取消”

我已经有了在线程池上按顺序执行委托的现有代码,并且管理最多2个排队任务的想法——一个当前正在执行,另一个是下一个。缺少的部分能够将任务的结果状态设置为“已取消”

不幸的是,在
任务调度器
中,您实际上几乎无法访问
任务
的状态或任何
取消令牌

我试图通过跟踪最后一个排队的任务来解决这个问题,并且在执行任务时,如果它不等于最后一个排队的任务,则抛出
TaskCancelledException
,但这似乎不起作用。我想这是因为异常不会在任务的委托中抛出,所有的“魔力”实际上都是在
TryExecuteTask()中处理的

以下是我得到的:

public class CurrentPendingTaskScheduler : TaskScheduler
        {
        private readonly ThreadSafeCurrentPendingQueueProcessor<Task> _Processor;
        private Task _LastTask;

        public CurrentPendingTaskScheduler()
            {
            _Processor = new ThreadSafeCurrentPendingQueueProcessor<Task>();
            _Processor.Process += _Processor_Process;
            }

        private void _Processor_Process(Task obj)
            {
            // If there's a newer task already, cancel this one before starting
            if (obj != _LastTask)
                throw new TaskCanceledException(obj);

            TryExecuteTask(obj);

            // If a newer task was added whilst we worked, cancel this one
            if (obj != _LastTask)
                throw new TaskCanceledException(obj);
            }

        protected override void QueueTask(Task task)
            {
            _LastTask = task;
            _Processor.Enqueue(task);
            }

        protected override Boolean TryExecuteTaskInline(Task task, Boolean taskWasPreviouslyQueued)
            {
            return false;
            }

        protected override IEnumerable<Task> GetScheduledTasks()
            {
            throw new NotImplementedException();
            }
        }
公共类CurrentPendingTaskScheduler:TaskScheduler
{
专用只读线程安全CurrentPendingQueueProcessor\u处理器;
私有任务_LastTask;
public CurrentPendingTaskScheduler()
{
_Processor=新的ThreadSafeCurrentPendingQueueProcessor();
_Process+=\u Processor\u进程;
}
私有无效处理程序(任务obj)
{
//如果已经有更新的任务,请在开始之前取消此任务
如果(obj!=\u LastTask)
抛出新的TaskCanceledException(obj);
TryExecuteTask(obj);
//如果在我们工作时添加了更新的任务,请取消此任务
如果(obj!=\u LastTask)
抛出新的TaskCanceledException(obj);
}
受保护的覆盖无效队列任务(任务任务)
{
_LastTask=任务;
_处理器排队(任务);
}
受保护的重写布尔TryExecuteTaskInline(任务任务,布尔任务先前排队)
{
返回false;
}
受保护的重写IEnumerable GetScheduledTasks()
{
抛出新的NotImplementedException();
}
}
ThreadSafeCurrentPendingQueueProcessor
类是一个帮助程序,它通过事件回调以处理单个后台线程上的排队项目,只允许一个活动项目和一个挂起项目

如果“最后一个任务”在处理器回调之前已更改,则异常只会阻止任务运行(但不会影响其状态)。如果回调确实开始运行,但“最后一个任务”在此期间发生了更改,那么在任何延续启动之后,异常抛出的时间就太晚了

我也不确定这是什么原因,但在第一次使用调度程序时(我为每个UI元素单击调度一个任务),
QueueTask
会被调用一次,同时调用新任务。但是,对于每个后续的调度,都会调用两次。当
\u LastTask
被覆盖时,这会把事情搞得更糟

我觉得
TaskCompletionSource
可能有一些用处,但不太清楚如何使用


是否可以实现一个按所述工作的
任务调度器
?我知道在创建任务时,我可以在调度程序之外实现这种行为,但我需要在许多地方使用它,并试图通过在可重用的调度程序中使用它来简化工作。

我将创建一个助手类,该类接受输入操作,启动它,并取消现有的操作,并覆盖其内部变量。由于您不能直接明确地对
任务执行
Cancel()
,因此您需要将自己的
TaskCancellationSource
放在手边。如果要提供外部令牌,可以将它们与
CancellationTokenSource.CreateLinkedTokenSource(…)
结合使用。如果需要关注结果,这将为TaskCompletionSource提供一个很好的机会

public class OverwriteTaskHandler<T>
{
    private Task<T> _task;
    private TaskCompletionSource<T> _tcs;
    private CancellationTokenSource _cts;

    public OverwriteTaskHandler(Func<T> operation)
    {
        _tcs = new TaskCompletionSource<T>();
        _cts = new CancellationTokenSource();
        TryPushTask(operation);
    }

    public bool TryPushTask(Func<T> operation)
    {
        if (_tcs.Task.IsCompleted)
            return false;   //It would be unsafe to use this instance as it is already "finished"
        _cts.Cancel();
        _cts = new CancellationTokenSource();
        _task = Task.Run(operation, _cts.Token);
        _task.ContinueWith(task => _tcs.SetResult(task.Result));
        return true;
    }

    public void Cancel()
    {
        _cts.Cancel();
    }

    public Task<T> WrappedTask { get { return _tcs.Task; } }
}
公共类OverwriteTaskHandler
{
私人任务(u Task),;
专用任务完成源\u tcs;
私有取消令牌源;
公共OverwriteTaskHandler(Func操作)
{
_tcs=新任务完成源();
_cts=新的CancellationTokenSource();
任务(操作);
}
公共bool任务(Func操作)
{
如果(_tcs.Task.IsCompleted)
return false;//使用此实例是不安全的,因为它已经“完成”
_cts.Cancel();
_cts=新的CancellationTokenSource();
_task=task.Run(操作,_cts.Token);
_task.ContinueWith(task=>\u tcs.SetResult(task.Result));
返回true;
}
公开作废取消()
{
_cts.Cancel();
}
公共任务WrappedTask{get{return}tcs.Task;}
}

Discalimer:我还没有测试过这个,所以请仔细检查一下

AFAIK TaskScheduler无法用于执行此操作。你真的需要别的东西。例如,您可以使用以下用法编写一个帮助器类:

static CurrentPendingTaskContext ctx = ...;

async Task MyAsyncFunc() {
 await ctx.RegisterAndMaybeCancel();
 try {
  //rest of method
 }
 finally {
  ctx.NotifyCompletion();
 }
}
RegisterAndMaybeCancel
将等待当前运行的任务完成。如果此特定任务已被另一个任务取代,它将抛出取消异常

我现在没有时间实现这个类(尽管它很诱人)。但我认为这种语法模式非常简单,可以在很多地方使用

你也可以
async Task MyAsyncFunc() {
 using (await ctx.RegisterAndMaybeCancel())
 {
      //rest of method
 }
}