C# 并行处理非相似项的任务工厂
数据库中不断添加大量需要处理的项目。我希望平行处理非相似项目 例如: A类项目:项目1、项目2、项目3 B类项目:项目4、项目5、项目6 C类项目:项目7、项目8、项目9 第1项、第4项和第7项应并行处理 随着一种类型的更多项被添加到数据库中,它们将被选中并排队,以便仅在处理此类型的先前项之后进行处理 我想我可以使用带有CustomTaskScheduler的静态任务工厂来实现这一点,它将仅在该类型的前一个任务完成后启动新任务?我的问题是我的CustomTaskScheduler应该是什么样子C# 并行处理非相似项的任务工厂,c#,multithreading,task-parallel-library,taskfactory,C#,Multithreading,Task Parallel Library,Taskfactory,数据库中不断添加大量需要处理的项目。我希望平行处理非相似项目 例如: A类项目:项目1、项目2、项目3 B类项目:项目4、项目5、项目6 C类项目:项目7、项目8、项目9 第1项、第4项和第7项应并行处理 随着一种类型的更多项被添加到数据库中,它们将被选中并排队,以便仅在处理此类型的先前项之后进行处理 我想我可以使用带有CustomTaskScheduler的静态任务工厂来实现这一点,它将仅在该类型的前一个任务完成后启动新任务?我的问题是我的CustomTaskScheduler应该是什么样子
class test
{
private static void Main()
{
//List of items from the database
var itemList = new List<Item>();
itemList.Add(new Item(1, "A"));
itemList.Add(new Item(2, "A"));
itemList.Add(new Item(3, "A"));
itemList.Add(new Item(4, "B"));
itemList.Add(new Item(5, "B"));
itemList.Add(new Item(6, "B"));
itemList.Add(new Item(7, "C"));
itemList.Add(new Item(8, "C"));
itemList.Add(new Item(9, "C"));
//This needs to be run on a timer picking up new items from the database every time
new ProcessQueue().ProcessAllItems(itemList);
Console.ReadLine();
}
}
public class ProcessQueue
{
private static CustomTaskScheduler customTaskScheduler = new CustomTaskScheduler(1);
private static TaskFactory factory = new TaskFactory(customTaskScheduler);
public void ProcessAllItems(List<Item> itemList)
{
var cts = new CancellationTokenSource();
foreach (var item in itemList)
{
factory.StartNew(
o =>
executeTask(item.Id, item.ItemType),
item.ItemType, //unique identifier for multiple threads
cts.Token);
}
}
public void executeTask(int id, string parentId)
{
Console.WriteLine("Item - {0} ItemType - {1} on thread {1} ", id, parentId,
Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
}
}
public class Item
{
public Item(int id, string itemType)
{
Id = id;
ItemType = itemType;
}
public int Id { get; set; }
public string ItemType { get; set; }
}
这个怎么样,改编自。它使用Task.AsyncState(根据您的示例,它应该是Item.ItemType)来同步线程
public class ParallelTypeScheduler : TaskScheduler {
// Indicates whether the current thread is processing work items.
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
// The list of tasks to be executed
private readonly ConcurrentDictionary<object, LinkedList<Task>> _tasks = new ConcurrentDictionary<object, LinkedList<Task>>; // protected by lock(_tasks)
// Indicates whether the scheduler is currently processing work items.
private readonly Dictionary<object, bool> _typesRunning = new Dictionary<object,bool>();
// Queues a task to the scheduler.
protected sealed override void QueueTask(Task task)
{
LinkedList<Task> typesTasks = _tasks.GetOrAdd(task.AsyncState, new LinkedList<Task>());
lock (typesTasks)
{
typesTasks.AddLast(task);
if(!_typesRunning.ContainsKey(task.AsyncState) || _typesRunning[task.AsyncState] == false){
_typesRunning[task.AsyncState] = true;
NotifyThreadPoolOfPendingWork(task.AsyncState);
}
}
}
// Inform the ThreadPool that there's work to be executed for this scheduler.
private void NotifyThreadPoolOfPendingWork(object type)
{
ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
_currentThreadIsProcessingItems = true;
try
{
LinkedList<Task> typedTasks;
if (_tasks.TryGetValue(type, out typedTasks)) {
while (true) {
Task item;
lock (typedTasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (_tasks.Count == 0)
{
_typesRunning[type] = false;
break;
}
// Get the next item from the queue
item = typedTasks.First.Value;
typedTasks.RemoveFirst();
}
base.TryExecuteTask(item);
}
}
}
// We're done processing items on the current thread
finally { _currentThreadIsProcessingItems = false; }
}, null);
}
// Attempts to execute the specified task on the current thread.
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!_currentThreadIsProcessingItems) return false;
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued) {
LinkedList<Task> typedTasks;
// Try to run the task.
if (_tasks.TryGetValue(task.AsyncState, out typedTasks) && TryDequeue(typedTasks, task))
return base.TryExecuteTask(task);
else
return false;
} else
return base.TryExecuteTask(task);
}
// Attempt to remove a previously scheduled task from the scheduler.
protected sealed override bool TryDequeue(LinkedList<Task> typedTasks, Task task)
{
lock (typedTasks) return typedTasks.Remove(task);
}
// Gets an enumerable of the tasks currently scheduled on this scheduler.
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_tasks, ref lockTaken);
if (lockTaken) return _tasks.SelectMany(t => t.Value);
else throw new NotSupportedException();
}
finally
{
if (lockTaken) Monitor.Exit(_tasks);
}
}
}
我不确定线程安全如何与GetScheduledTasks一起工作。GetScheduledTasks获得锁并继续返回_tasks,这非常有趣。SelectMany在调用方可以迭代SelectMany返回的可枚举项之前立即释放锁。这违背了一开始就拥有锁的目的,并不是说我会相信任何拼写为parrelell parallel的人都能得到正确的锁。@Krill Shlenskiy,是的,这很奇怪,我引用的MSDN示例也做了同样的事情。我没有研究它,只是查阅了GetScheduledTasks的文档,显然该方法只提供给调试器支持,所以假设所有其他线程都将挂起。但这并不能解释为什么在这种情况下锁是必要的。。