C#锁定线程问题

时间:2015-01-18 07:07:44

标签: c# multithreading dictionary locks

这里的任务非常简单(或者我认为......)。我想用要执行的方法填充队列(所有这些都将返回一个对象结果),然后我想从这个队列中拉出一些任意数量的线程,执行方法,并将结果添加到其他一些集合中(在这种情况下是一本字典),当所有工作完成后将返回。将在主线程中调用一个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;工作方法似乎解决了它,这是奇怪的。这是我第一次使用线程,所以我不确定我是否可怕地滥用锁定,或者是什么。如果有人能够对我做错的事情提供一些见解,那将非常感激。

2 个答案:

答案 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)

带有Parallel类的

版本:

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());
    }
});