9 changed files with 185 additions and 9 deletions
@ -0,0 +1,67 @@
@@ -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 @@
@@ -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