static class · System.Threading.Tasks

Scheduler

public static class Scheduler

Runs tasks on this thread, one at a time: those ready (in order), then the timers that are due; when nothing is ready it sleeps until the next timer.

Tipo
static class
Espacio de nombres
System.Threading.Tasks
Ensamblado
System.Core
Acceso
public, static, abstract
Archivo fuente
Tasks.ant

Herencia

Clases base
Any Scheduler

Propiedades

bool Threaded { get; }
#

static read-only

Mutex Sync { get; }
#

static read-only

Condition Changed { get; }
#

static read-only

bool Sleeping { get; }
#

static read-only

Whether a thread sleeps on Changed (read holding Sync).

Métodos

OnItsThread() : bool
#

static

Whether the caller is the scheduler's thread (the program's first).

Ready(Task task)
#

static

Runs task in its turn (Run()). On another thread (holding Sync): it goes to the scheduler's inbox.

Started()
#

static

A Task.Run body starts, and has finished.

Finished()
#

static

BlockUntil(Task task)
#

static

Waits (another thread than the scheduler's) until task has finished.

Soon(Task task)
#

static

Done in its turn (after what is ready now).

After(Task task, i32 milliseconds)
#

static

Done after milliseconds.

RunUntil(Task task)
#

static

Runs ready tasks and timers until task has finished (or nothing is left that could finish it).

Soonest() : i32
#

static

The timer that is due first.

Due(i64 now) : bool
#

static

Completes the timers whose time has come (the first due first): whether there was one.

Timers()
#

static

Completes the timers that are due; when none is, sleeps until the first one. Once threads complete tasks, the sleep is on Changed (a task completed on another thread wakes it): the timer only bounds it.

Miembros privados e internos (12)

List<Task> ready
#

private static

i32 first
#

private static

List<Task> timers
#

private static

List<i64> due
#

private static

bool threaded
#

private static

Once Task.Run was used: tasks made ready on other threads (under Sync), and how many Task.Run bodies are running.

Mutex sync
#

private static

Condition changed
#

private static

List<Task> inbox
#

private static

i32 running
#

private static

i32 sleepers
#

private static

Threads asleep on Changed (under Sync): a change wakes them only if there are some.

Sleep()
#

private static

Sleeps on Changed (holding Sync).

Drain()
#

private static

Moves what other threads made ready to the ready list.

Miembros heredados (5)

Código fuente

Scheduler en Antril, descompilado a partir de su Larva HIR.

System.Threading.Tasks.Scheduler.ant
// System.Threading.Tasks.Scheduler — System.Core (decompiled from Larva HIR)

import System;
import System.Threading;

into namespace System.Threading.Tasks
{
	/// Runs tasks on this thread, one at a time: those ready (in order), then the timers that are due; when nothing is
	/// ready it sleeps until the next timer.
	public static class Scheduler
	{
		List<Task> ready;
		i32 first;
		List<Task> timers;
		List<i64> due;

		/// Once Task.Run was used: tasks made ready on other threads (under Sync), and how many Task.Run bodies are running.
		bool threaded;
		Mutex sync;
		Condition changed;
		List<Task> inbox;
		i32 running;

		/// Threads asleep on Changed (under Sync): a change wakes them only if there are some.
		i32 sleepers;

		/// Sleeps on Changed (holding Sync).
		Sleep()
		{
			sleepers++;
			changed.Wait(sync);
			sleepers--;
		}

		/// Moves what other threads made ready to the ready list.
		[NoInline]
		Drain()
		{
			sync.Lock();
			{
				ListIterator<Task> _iterator0 = inbox.Iterator();
				loop (_iterator0.Next())
				{
					Task task = _iterator0.Current;
					ready.Add(task);
				}
			}
			inbox.Clear();
			sync.Unlock();
		}

		public:
			/// Whether the caller is the scheduler's thread (the program's first).
			OnItsThread() : bool => Thread.CurrentId == 1;

			/// Runs `task` in its turn (`Run()`). On another thread (holding Sync): it goes to the scheduler's inbox.
			Ready(Task task)
			{
				if (threaded && !OnItsThread())
					inbox.Add(task);
				else
					ready.Add(task);
			}

			/// A Task.Run body starts, and has finished.
			Started()
			{
				sync.Lock();
				threaded = true;
				running++;
				sync.Unlock();
			}

			Finished()
			{
				sync.Lock();
				running--;
				bool sleeping = sleepers > 0;
				sync.Unlock();
				if (sleeping)
					changed.Broadcast();
			}

			/// Waits (another thread than the scheduler's) until `task` has finished.
			BlockUntil(Task task)
			{
				loop (i32 spin = 0; spin < 1000 && !task.IsCompleted; spin++)
					Thread.SpinWait(20);
				loop (i32 spin = 0; spin < 32 && !task.IsCompleted; spin++)
					Thread.Yield();
				sync.Lock();
				loop (!task.IsCompleted)
					Sleep();
				sync.Unlock();
			}

			/// Done in its turn (after what is ready now).
			Soon(Task task)
			{
				ready.Add(new CompleteTask(task));
			}

			/// Done after `milliseconds`.
			After(Task task, i32 milliseconds)
			{
				timers.Add(task);
				due.Add(Environment.TickCount64 + (i64)milliseconds);
			}

			/// Runs ready tasks and timers until `task` has finished (or nothing is left that could finish it).
			RunUntil(Task task)
			{
				i32 ran = 0;
				loop (!task.IsCompleted)
				{
					if (threaded && inbox.Count > 0)
						Drain();
					if (first < ready.Count)
					{
						Task next = ready[first];
						first++;
						bool passEnded = first == ready.Count;
						if (passEnded)
						{
							ready.Clear();
							first = 0;
						}
						next.Run();
						ran++;
						if (timers.Count > 0 && (passEnded || ran >= 32))
						{
							ran = 0;
							Due(Environment.TickCount64);
						}
						continue;
					}
					ran = 0;
					if (threaded && timers.Count == 0)
					{
						loop (i32 spin = 0; spin < 1000 && inbox.Count == 0 && !task.IsCompleted; spin++)
							Thread.SpinWait(20);
						loop (i32 spin = 0; spin < 32 && inbox.Count == 0 && !task.IsCompleted; spin++)
							Thread.Yield();
						sync.Lock();
						loop (inbox.Count == 0 && !task.IsCompleted && running > 0)
							Sleep();
						bool stuck = inbox.Count == 0 && !task.IsCompleted;
						sync.Unlock();
						if (stuck)
							throw new InvalidOperationError("Task.Wait: the task waits for something that never finishes");
						continue;
					}
					if (timers.Count == 0)
						throw new InvalidOperationError("Task.Wait: the task waits for something that never finishes");
					Timers();
				}
			}

			/// The timer that is due first.
			Soonest() : i32
			{
				i32 soonest = 0;
				loop (i32 t = 1..timers.Count)
				{
					if (due[t] < due[soonest])
						soonest = t;
				}
				return soonest;
			}

			/// Completes the timers whose time has come (the first due first): whether there was one.
			Due(i64 now) : bool
			{
				bool any = false;
				loop (timers.Count > 0)
				{
					i32 soonest = Soonest();
					if (due[soonest] > now)
						break;
					Task timer = timers[soonest];
					timers.RemoveAt(soonest);
					due.RemoveAt(soonest);
					timer.Complete();
					any = true;
				}
				return any;
			}

			/// Completes the timers that are due; when none is, sleeps until the first one. Once threads complete tasks, the
			/// sleep is on Changed (a task completed on another thread wakes it): the timer only bounds it.
			Timers()
			{
				i64 now = Environment.TickCount64;
				if (Due(now))
					return;
				i64 soon = due[Soonest()];
				if (soon <= now)
				{
					Due(soon);
					return;
				}
				if (!threaded)
				{
					Thread.Sleep((i32)(soon - now));
					Due(soon);
					return;
				}
				sync.Lock();
				if (inbox.Count == 0)
				{
					sleepers++;
					changed.Wait(sync, (i32)(soon - now));
					sleepers--;
				}
				sync.Unlock();
				Due(Environment.TickCount64);
			}

			bool Threaded
			{
				get => threaded;
			}

			Mutex Sync
			{
				get => sync;
			}

			Condition Changed
			{
				get => changed;
			}

			/// Whether a thread sleeps on Changed (read holding Sync).
			bool Sleeping
			{
				get => sleepers > 0;
			}
	}
}
Esc