9 changed files with 185 additions and 9 deletions
@ -0,0 +1,67 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Util |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A task scheduler that manages its own thread pool.
|
||||||
|
/// </summary>
|
||||||
|
public class CustomThreadPoolTaskScheduler : SimpleTaskScheduler |
||||||
|
{ |
||||||
|
int currentThreadCount; |
||||||
|
readonly int maxThreadCount; |
||||||
|
|
||||||
|
public CustomThreadPoolTaskScheduler(int maxThreadCount) |
||||||
|
{ |
||||||
|
this.maxThreadCount = maxThreadCount; |
||||||
|
} |
||||||
|
|
||||||
|
public override int MaximumConcurrencyLevel { |
||||||
|
get { return maxThreadCount; } |
||||||
|
} |
||||||
|
|
||||||
|
protected override void QueueTask(Task task) |
||||||
|
{ |
||||||
|
base.QueueTask(task); |
||||||
|
if (IncrementThreadCount()) { |
||||||
|
// Successfully incremented the thread count, we may start a thread
|
||||||
|
StartThread(RunThread); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void StartThread(ThreadStart start) |
||||||
|
{ |
||||||
|
var t = new Thread(RunThread); |
||||||
|
t.IsBackground = true; |
||||||
|
t.Start(); |
||||||
|
} |
||||||
|
|
||||||
|
bool IncrementThreadCount() |
||||||
|
{ |
||||||
|
int c = Volatile.Read(ref currentThreadCount); |
||||||
|
while (c < maxThreadCount) { |
||||||
|
if (Interlocked.CompareExchange(ref currentThreadCount, c + 1, c) == c) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void RunThread() |
||||||
|
{ |
||||||
|
do { |
||||||
|
// Run tasks while they are available:
|
||||||
|
while (TryRunNextTask()); |
||||||
|
// Decrement the thread count:
|
||||||
|
Interlocked.Decrement(ref currentThreadCount); |
||||||
|
// Tasks might have been added while we were decrementing the thread count,
|
||||||
|
// so if the queue isn't empty anymore, resume this thread
|
||||||
|
} while(ScheduledTaskCount > 0 && IncrementThreadCount()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,82 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Util |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A simple scheduler that adds tasks to a queue.
|
||||||
|
/// This scheduler does not create any worker threads on its own,
|
||||||
|
/// but requires external code to call <see cref="RunNextTask"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class SimpleTaskScheduler : TaskScheduler, IDisposable |
||||||
|
{ |
||||||
|
[ThreadStatic] |
||||||
|
static SimpleTaskScheduler activeScheduler; |
||||||
|
|
||||||
|
BlockingCollection<Task> queue = new BlockingCollection<Task>(); |
||||||
|
|
||||||
|
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) |
||||||
|
{ |
||||||
|
return activeScheduler == this && base.TryExecuteTask(task); |
||||||
|
} |
||||||
|
|
||||||
|
protected override void QueueTask(Task task) |
||||||
|
{ |
||||||
|
queue.Add(task); |
||||||
|
} |
||||||
|
|
||||||
|
protected override IEnumerable<Task> GetScheduledTasks() |
||||||
|
{ |
||||||
|
return queue; |
||||||
|
} |
||||||
|
|
||||||
|
protected int ScheduledTaskCount { |
||||||
|
get { return queue.Count; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs the next task in the queue.
|
||||||
|
/// If no task is available, this method will block.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">Cancellation token that can be used to cancel
|
||||||
|
/// waiting for a task to become available. It cannot be used to cancel task execution!</param>
|
||||||
|
public void RunNextTask(CancellationToken cancellationToken = default(CancellationToken)) |
||||||
|
{ |
||||||
|
Task task = queue.Take(cancellationToken); |
||||||
|
RunTask(task); |
||||||
|
} |
||||||
|
|
||||||
|
public bool TryRunNextTask() |
||||||
|
{ |
||||||
|
Task task; |
||||||
|
if (queue.TryTake(out task)) { |
||||||
|
RunTask(task); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void RunTask(Task task) |
||||||
|
{ |
||||||
|
var oldActiveScheduler = activeScheduler; |
||||||
|
activeScheduler = this; |
||||||
|
try { |
||||||
|
base.TryExecuteTask(task); |
||||||
|
} finally { |
||||||
|
activeScheduler = oldActiveScheduler; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public virtual void Dispose() |
||||||
|
{ |
||||||
|
queue.Dispose(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue