这里的任务非常简单(或者我认为......)。我想用要执行的方法填充队列(所有这些都将返回一个对象结果),然后我想从这个队列中拉出一些任意数量的线程,执行方法,并将结果添加到其他一些集合中(在这种情况下是一本字典),当所有工作完成后将返回。将在主线程中调用一个main方法,该方法将启动处理并应阻塞,直到所有线程完成它们正在执行的任何操作并返回带有结果的集合。所以我把这个课程放在一起:
public class BackgroundWorkManager
{
public delegate object ThreadTask();
private Thread[] workers;
private ManualResetEvent workerThreadMre;
private ManualResetEvent mainThreadMre;
private Queue<WorkItem> workQueue;
private Dictionary<string, object> results;
private object writeLock;
private int activeTasks;
private struct WorkItem
{
public string name;
public ThreadTask task;
public WorkItem(string name, ThreadTask task)
{
this.name = name;
this.task = task;
}
}
private void workMethod()
{
while (true)
{
workerThreadMre.WaitOne();
WorkItem task;
lock (workQueue)
{
if (workQueue.Count == 0)
{
workerThreadMre.Reset();
continue;
}
task = workQueue.Dequeue();
}
object result = task.task();
lock (writeLock)
{
results.Add(task.name, result);
activeTasks--;
if (activeTasks == 0)
mainThreadMre.Set();
}
}
}
public BackgroundWorkManager()
{
workers = new Thread[Environment.ProcessorCount];
workerThreadMre = new ManualResetEvent(false);
mainThreadMre = new ManualResetEvent(false);
workQueue = new Queue<WorkItem>();
writeLock = new object();
activeTasks = 0;
for (int i = 0; i < Environment.ProcessorCount; i++)
{
workers[i] = new Thread(workMethod);
workers[i].Priority = ThreadPriority.Highest;
workers[i].Start();
}
}
public void addTask(string name, ThreadTask task)
{
workQueue.Enqueue(new WorkItem(name, task));
}
public Dictionary<string, object> process()
{
results = new Dictionary<string, object>();
activeTasks = workQueue.Count;
mainThreadMre.Reset();
workerThreadMre.Set();
mainThreadMre.WaitOne();
workerThreadMre.Reset();
return results;
}
}
如果我使用对象一次处理方法队列,但是如果我尝试这样的话,那么这个工作正常
BackgroundWorkManager manager = new BackgroundWorkManager();
for (int i = 0; i < 20; i++)
{
manager.addTask("result1", (BackgroundWorkManager.ThreadTask)delegate
{
return (object)(1);
});
manager.process();
}
事情破裂了。我要么死锁,要么我得到一个例外,说我写的结果字典已经包含了密钥(但Visual Studio调试器说它是空的)。添加&#39; Thread.Sleep(1)&#39;工作方法似乎解决了它,这是奇怪的。这是我第一次使用线程,所以我不确定我是否可怕地滥用锁定,或者是什么。如果有人能够对我做错的事情提供一些见解,那将非常感激。
答案 0 :(得分:1)
如何使用生产者 - 消费者模式有很多选择。例如,您可以使用ActionBlock<T>
(TPL Dataflow
的一部分)来大幅简化代码:
var concurrentDictionary = new ConcurrentDictionary<string, object>();
ActionBlock<Func<object>> actionBlock = new ActionBlock<Func<object>>((func) =>
{
var obj = func();
concurrentDictionary.AddOrUpdate("someKey", obj, (s,o) => o);
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism =
Environment.ProcessorCount });
然后简单地发布你的代表:
foreach (var task in tasks)
{
actionBlock.Post(() => (object) 1);
}
答案 1 :(得分:1)
版本:
List<Func<object>> actions = new List<Func<object>>();
actions.Add(delegate { return (object)(1); });
actions.Add(delegate { return (object)(1); });
actions.Add(delegate { return (object)(1); });
Dictionary<string, object> results = new Dictionary<string,object>();
Parallel.ForEach(actions,(f)=> {
lock (results)
{
results.Add(Guid.NewGuid().ToString(), f());
}
});