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

0

この問題に遭遇したが、修正されていないバージョンの Mono (Mono 3.0.12 まで) を使用するように制約されている場合は、次のハックを使用できます。スタック トレースを読み取る必要があるため非効率的です。そのため、ほとんどの場合、チェックを回避するようにしてください。

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 に答える