网上演练
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
7.0 KiB

#if NET20 || NET30 || NET35 || !NET_4_6
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Diagnostics;
namespace System.Threading.Tasks
{
/// <summary>
/// Represents an abstract scheduler for tasks.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> acts as the extension point for all
/// pluggable scheduling logic. This includes mechanisms such as how to schedule a task for execution, and
/// how scheduled tasks should be exposed to debuggers.
/// </para>
/// <para>
/// All members of the abstract <see cref="TaskScheduler"/> type are thread-safe
/// and may be used from multiple threads concurrently.
/// </para>
/// </remarks>
[DebuggerDisplay("Id={Id}")]
[DebuggerTypeProxy(typeof(SystemThreadingTasksTaskSchedulerDebugView))]
public abstract partial class TaskScheduler
{
/// <summary>
/// Indicates the maximum concurrency level this
/// <see cref="TaskScheduler"/> is able to support.
/// </summary>
public virtual int MaximumConcurrencyLevel
{
get { return int.MaxValue; }
}
/// <summary>
/// Notifies the scheduler that a work item has made progress.
/// </summary>
internal virtual void NotifyWorkItemProgress()
{
}
#pragma warning disable 0414
// An AppDomain-wide default manager.
private static readonly TaskScheduler _defaultTaskScheduler = new ThreadPoolTaskScheduler();
#pragma warning restore 0414
/// <summary>
/// Gets the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>
/// associated with the currently executing task.
/// </summary>
/// <remarks>
/// When not called from within a task, <see cref="InternalCurrent"/> will return null.
/// </remarks>
internal static TaskScheduler InternalCurrent
{
get
{
var currentTask = Task.InternalCurrent;
return ((currentTask != null)
&& ((currentTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0)
) ? currentTask.ExecutingTaskScheduler : null;
}
}
private static readonly object _unobservedTaskExceptionLockObject = new object();
private static EventHandler<UnobservedTaskExceptionEventArgs> _unobservedTaskException;
/// <summary>
/// Occurs when a faulted <see cref="System.Threading.Tasks.Task"/>'s unobserved exception is about to trigger exception escalation
/// policy, which, by default, would terminate the process.
/// </summary>
/// <remarks>
/// This AppDomain-wide event provides a mechanism to prevent exception
/// escalation policy (which, by default, terminates the process) from triggering.
/// Each handler is passed a <see cref="T:System.Threading.Tasks.UnobservedTaskExceptionEventArgs"/>
/// instance, which may be used to examine the exception and to mark it as observed.
/// </remarks>
public static event EventHandler<UnobservedTaskExceptionEventArgs> UnobservedTaskException
{
[Security.SecurityCritical]
add
{
if (value != null)
{
lock (_unobservedTaskExceptionLockObject)
{
_unobservedTaskException += value;
}
}
}
[Security.SecurityCritical]
remove
{
lock (_unobservedTaskExceptionLockObject)
{
_unobservedTaskException -= value;
}
}
}
internal static void PublishUnobservedTaskException(Task sender, UnobservedTaskExceptionEventArgs ueea)
{
// Lock this logic to prevent just-unregistered handlers from being called.
lock (_unobservedTaskExceptionLockObject)
{
// Since we are under lock, it is technically no longer necessary
// to make a copy. It is done here for convenience.
var handler = _unobservedTaskException;
if (handler != null)
{
handler(sender, ueea);
}
}
}
/// <summary>
/// Provides an array of all queued <see cref="System.Threading.Tasks.Task">Task</see> instances
/// for the debugger.
/// </summary>
/// <remarks>
/// The returned array is populated through a call to <see cref="GetScheduledTasks"/>.
/// Note that this function is only meant to be invoked by a debugger remotely.
/// It should not be called by any other codepaths.
/// </remarks>
/// <returns>An array of <see cref="System.Threading.Tasks.Task">Task</see> instances.</returns>
/// <exception cref="T:System.NotSupportedException">
/// This scheduler is unable to generate a list of queued tasks at this time.
/// </exception>
internal Task[] GetScheduledTasksForDebugger()
{
// this can throw InvalidOperationException indicating that they are unable to provide the info
// at the moment. We should let the debugger receive that exception so that it can indicate it in the UI
var activeTasksSource = GetScheduledTasks();
if (activeTasksSource == null)
{
return null;
}
// If it can be cast to an array, use it directly
var activeTasksArray = activeTasksSource as Task[] ?? (new List<Task>(activeTasksSource)).ToArray();
// touch all Task.Id fields so that the debugger doesn't need to do a lot of cross-proc calls to generate them
foreach (var t in activeTasksArray)
{
GC.KeepAlive(t.Id);
}
return activeTasksArray;
}
/// <summary>
/// Nested class that provides debugger view for TaskScheduler
/// </summary>
internal sealed class SystemThreadingTasksTaskSchedulerDebugView
{
private readonly TaskScheduler _taskScheduler;
public SystemThreadingTasksTaskSchedulerDebugView(TaskScheduler scheduler)
{
_taskScheduler = scheduler;
}
// returns the scheduler's Id
public int Id
{
get { return _taskScheduler.Id; }
}
// returns the scheduler's GetScheduledTasks
public IEnumerable<Task> ScheduledTasks
{
get { return _taskScheduler.GetScheduledTasks(); }
}
}
}
}
#endif