[英]Limited concurrency TaskScheduler that can interleave tasks to be explicitly ordered
我正在尋找一個TaskScheduler
:
LimitedConcurrencyLevelTaskScheduler
(使用線程池線程)或WorkStealingTaskScheduler
做到這一點。 目前,我們使用TaskScheduler.Default
為廣大池(在線程池的增長算法等的支配)和new OrderedTaskScheduler()
每當我們要訂購的任務。 我想保持這種行為,但將這兩個要求限制在我自己的專用線程池中。
QueuedTaskScheduler
似乎非常接近。 我認為返回子TaskScheduler的QueuedTaskScheduler.ActivateNewQueue()
方法將在父工作池上執行任務IN ORDER,但似乎並非如此。 子TaskSchedulers似乎具有與父級相同的並行化級別。
我不一定希望子任務調度程序任務優先於父任務調度程序任務(盡管它將來可能是一個很好的功能)。
我在這里看到了一個相關的問題: 有限的並發級別任務調度程序(具有任務優先級)處理包裝的任務,但我的要求不需要處理異步任務(我的所有排隊任務從頭到尾完全同步,沒有延續)。
我假設“完全有序”你也意味着“一次一個”。
在這種情況下,我相信有一個內置的解決方案應該做得很好: ConcurrentExclusiveSchedulerPair
。
您的“父”調度程序將是並發調度程序:
TaskScheduler _parent = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 8)
.ConcurrentScheduler;
並且“子”調度程序將是一個獨占調度程序,它使用下面的並發調度程序:
var myScheduler = new ConcurrentExclusiveSchedulerPair(_parent).ExclusiveScheduler;
在仔細考慮了其他答案后,我決定使用它更容易創建自定義QueuedTaskScheduler
因為我不需要擔心異步任務或IO完成(盡管這給了我一些思考)。
首先,當我們從子工作池中獲取工作時,我們在FindNextTask_NeedsLock
添加一個基於信號量的鎖:
var items = queueForTargetTask._workItems;
if (items.Count > 0
&& queueForTargetTask.TryLock() /* This is added */)
{
targetTask = items.Dequeue();
對於專用線程版本,在ThreadBasedDispatchLoop
:
// ... and if we found one, run it
if (targetTask != null)
{
queueForTargetTask.ExecuteTask(targetTask);
queueForTargetTask.Release();
}
對於任務調度程序版本,在ProcessPrioritizedAndBatchedTasks
:
// Now if we finally have a task, run it. If the task
// was associated with one of the round-robin schedulers, we need to use it
// as a thunk to execute its task.
if (targetTask != null)
{
if (queueForTargetTask != null)
{
queueForTargetTask.ExecuteTask(targetTask);
queueForTargetTask.Release();
}
else
{
TryExecuteTask(targetTask);
}
}
我們在哪里創建新的子隊列:
/// <summary>Creates and activates a new scheduling queue for this scheduler.</summary>
/// <returns>The newly created and activated queue at priority 0 and max concurrency of 1.</returns>
public TaskScheduler ActivateNewQueue() { return ActivateNewQueue(0, 1); }
/// <summary>Creates and activates a new scheduling queue for this scheduler.</summary>
/// <param name="priority">The priority level for the new queue.</param>
/// <returns>The newly created and activated queue at the specified priority.</returns>
public TaskScheduler ActivateNewQueue(int priority, int maxConcurrency)
{
// Create the queue
var createdQueue = new QueuedTaskSchedulerQueue(priority, maxConcurrency, this);
...
}
最后,在嵌套的QueuedTaskSchedulerQueue
:
// This is added.
private readonly int _maxConcurrency;
private readonly Semaphore _semaphore;
internal bool TryLock()
{
return _semaphore.WaitOne(0);
}
internal void Release()
{
_semaphore.Release();
_pool.NotifyNewWorkItem();
}
/// <summary>Initializes the queue.</summary>
/// <param name="priority">The priority associated with this queue.</param>
/// <param name="maxConcurrency">Max concurrency for this scheduler.</param>
/// <param name="pool">The scheduler with which this queue is associated.</param>
internal QueuedTaskSchedulerQueue(int priority, int maxConcurrency, QueuedTaskScheduler pool)
{
_priority = priority;
_pool = pool;
_workItems = new Queue<Task>();
// This is added.
_maxConcurrency = maxConcurrency;
_semaphore = new Semaphore(_maxConcurrency, _maxConcurrency);
}
我希望這對於那些嘗試和我一樣的人來說非常有用,並且可以在單個易於使用的調度程序(可以使用默認的線程池或任何其他調度程序)上將無序任務與有序任務交錯。
===更新===
受Stephen Cleary的啟發,我最終使用:
private static readonly Lazy<TaskScheduler> Scheduler = new Lazy<TaskScheduler>(
() => new WorkStealingTaskScheduler(16));
public static TaskScheduler Default
{
get
{
return Scheduler.Value;
}
}
public static TaskScheduler CreateNewOrderedTaskScheduler()
{
return new QueuedTaskScheduler(Default, 1);
}
我理解你的任務有依賴性,這就是你想(部分)訂購它們的原因。 你可以用ContinueWith鏈做到這一點。 您只需要跟蹤任何給定鏈中的最新任務。 當一個新的進入時,您設置該任務的下一個延續並存儲新任務。 你放棄舊的。
替代解決方案:每個鏈都有一個SemaphoreSlim
並使用await sem.WaitAsync()
來非常靈活地手動控制DOP。 請注意,在一個信號燈不會阻止任何線程異步等待。 它只會占用一點內存。 根本沒有使用OS資源。 您可以使用極多的信號量。
我不認為調度程序是正確的抽象。 調度程序用於基於CPU的工作。 其他協調工具可以與任何Task
包括異步IO。 考慮更喜歡普通的任務組合器和協調原語。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.