0

InvalidOperationException在我正在实现的自定义上运行任务时,我偶尔会遇到以下TaskScheduler情况。

调查此问题后,它似乎是 Mono 实现中的一个错误。当一个延续任务被标记时TaskContinuationOptions.ExecuteSynchronously,它会被传递给任务调度器的TryExecuteTaskInline方法;但是,如果后者拒绝执行它并返回false,那么将不可避免地遇到以下异常(根据下面的代码摘录)。

有人可以建议一种在 Mono 上解决此问题的方法吗?我正在考虑改变我的TryExecuteTaskInline实现,使其始终接受执行同步延续;但是,我还没有找到一种方法来确定一项任务是否一个延续(不使用反射)。

System.InvalidOperationException: Start may not be called on a continuation task
  at System.Threading.Tasks.Task.Start (System.Threading.Tasks.TaskScheduler scheduler) [0x00000] in <filename unknown>:0 
  at System.Threading.Tasks.Task.RunSynchronouslyCore (System.Threading.Tasks.TaskScheduler scheduler) [0x00000] in <filename unknown>:0 
  at System.Threading.Tasks.TaskContinuation.Execute () [0x00000] in <filename unknown>:0 
  at System.Threading.Tasks.Task.ProcessCompleteDelegates () [0x00000] in <filename unknown>:0 
  at System.Threading.Tasks.Task.Finish () [0x00000] in <filename unknown>:0 
  at System.Threading.Tasks.Task.ThreadStart () [0x00000] in <filename unknown>:0 
  at System.Threading.Tasks.Task.Execute () [0x00000] in <filename unknown>:0 
  at System.Threading.Tasks.TaskScheduler.TryExecuteTask (System.Threading.Tasks.Task task) [0x00000] in <filename unknown>:0 
  at System.Threading.Tasks.Schedulers.WorkStealingTaskScheduler.DispatchLoop (CancellationToken cancellationToken) [0x00000] in <filename unknown>:0 

我粘贴在相关(精简)部分下方......

…来自TaskContinuation.cs

class TaskContinuation
{
    public void Execute ()
    {
        // ...

        if ((continuationOptions & TaskContinuationOptions.ExecuteSynchronously) != 0)
            task.RunSynchronouslyCore (task.scheduler);
        else
            task.Schedule ();
    }
}

…来自Task.cs

public class Task
{
    internal void RunSynchronouslyCore(TaskScheduler scheduler)
    {
        // ...

        if (scheduler.RunInline(this, false))
            return;

        Start(scheduler);
        Wait();
    }

    public void Start(TaskScheduler scheduler)
    {
        // ...

        if (IsContinuation)
            throw new InvalidOperationException("Start may not be called on a continuation task");

        SetupScheduler(scheduler);
        Schedule();
    }
}

……从TaskScheduler.cs

public abstract class TaskScheduler
{
    protected abstract bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued);

    internal bool RunInline(Task task, bool taskWasPreviouslyQueued)
    {
        // ...

        return TryExecuteTaskInline(task, taskWasPreviouslyQueued);
    }
}
4

2 回答 2

1

它看起来很容易修复。唯一需要更改的是更改https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Threading.Tasks/Task.cs#L228以调用方法 Schedule 而不是 Start

于 2013-07-16T07:16:43.297 回答
0

对于遇到此问题但被限制使用 Mono 的未修复版本(直到并包括 Mono 3.0.12)的任何人,您可以使用以下 hack。由于需要读取堆栈跟踪,因此效率低下,因此在大多数情况下尽量避免检查。

public class MyTaskScheduler : TaskScheduler
{
    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        if (this.CanInlineTasks() ||    // should return true most of the time
            IsSynchronousContinuationTask(task))
            return base.TryExecuteTaskInline(task, taskWasPreviouslyQueued);

        return false; 
    }

    private static bool IsSynchronousContinuationTask(Task task)
    {
        // assuming Mono
        string stackTrace = Environment.StackTrace;
        return stackTrace.Contains("System.Threading.Tasks.Task.RunSynchronouslyCore") 
            && stackTrace.Contains("System.Threading.Tasks.TaskContinuation.Execute");
    }
}
于 2013-07-16T11:30:22.850 回答