[英]Mono: Issue with synchronous continuation tasks
在正在实现的自定义TaskScheduler
上运行任务时,偶尔会遇到以下InvalidOperationException
。
在调查了这个问题之后,这似乎是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);
}
}
修复起来似乎很简单。 唯一需要做的更改就是将https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Threading.Tasks/Task.cs#L228更改为调用Schedule方法而不是Start方法
对于遇到此问题但被限制使用未固定版本的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");
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.