Linq上的线程安全和集合上的任务

时间:2015-04-29 01:49:20

标签: c# .net multithreading task-parallel-library

给出一些像这样的代码

public class CustomCollectionClass : Collection<CustomData> {}
public class CustomData
{
    string name;
    bool finished;
    string result;
}

public async Task DoWorkInParallel(CustomCollectionClass collection)
{
    // collection can be retrieved from a DB, may not exist.
    if (collection == null)
    {
        collection = new CustomCollectionClass();
        foreach (var data in myData) 
        { 
            collection.Add(new CustomData()
            {
                name = data.Name;
            });
        }
    }

    // This part doesn't feel safe. Not sure what to do here.
    var processTasks = myData.Select(o => 
        this.DoWorkOnItemInCollection(collection.Single(d => d.name = o.Name))).ToArray();

    await Task.WhenAll(processTasks);

    await SaveModifedCollection(collection);
}

public async Task DoWorkOnItemInCollection(CustomData data)
{
    await DoABunchOfWorkElsewhere();
    // This doesn't feel safe either. Lock here?
    data.finished = true;
    data.result = "Parallel";
}

正如我在一些内联评论中所指出的那样,对我来说做上述事情并不安全,但我不确定。我有一些元素集合,我希望为每个并行任务分配一个唯一元素,并让这些任务能够根据完成的工作修改集合中的单个元素。最终的结果是,我希望在个别之后保存集合,并行修改不同的元素。如果这不是一个安全的方法,那么我最好怎么做呢?

2 个答案:

答案 0 :(得分:0)

您的上述代码应该可以正常运行。您将一个项目传递给每个工作线程。我不太确定async属性。您可能只返回一个任务,然后在您的方法中执行:

public Task DoWorkOnItemInCollection(CustomData data)
{
    return Task.Run(() => {
        DoABunchOfWorkElsewhere().Wait();
        data.finished = true;
        data.result = "Parallel";
    });
}

您可能需要小心,对于大量项目,您可能会使用后台线程溢出最大线程数。在这种情况下,c#只会删除您的线程,以后可能很难调试。

我以前做过这个,如果不是将整个集合交给一些神奇的linq,而是将一个典型的消费者问题解决,可能会更容易:

class ParallelWorker<T>
{
    private Action<T> Action;
    private Queue<T> Queue = new Queue<T>();
    private object QueueLock = new object();
    private void DoWork() 
    {
        while(true)
        {
            T item;
            lock(this.QueueLock)
            {
                if(this.Queue.Count == 0) return; //exit thread
                item = this.Queue.DeQueue();
            }

            try { this.Action(item); }
            catch { /*...*/ }
        }
    }

    public void DoParallelWork(IEnumerable<T> items, int maxDegreesOfParallelism, Action<T> action)
    {
        this.Action = action;

        this.Queue.Clear();
        this.Queue.AddRange(items);

        List<Thread> threads = new List<Thread>();
        for(int i = 0; i < items; i++)
        {
            ParameterizedThreadStart threadStart = new ParameterizedThreadStart(DoWork);
            Thread thread = new Thread(threadStart);
            thread.Start();
            threads.Add(thread);
        }

        foreach(Thread thread in threads)
        {
            thread.Join();
        }
    }
}

这是免费的IDE,因此可能存在拼写错误。

答案 1 :(得分:0)

我将建议您使用Microsoft的Reactive Framework(NuGet&#34; Rx-Main&#34;)来执行此任务。

以下是代码:

'filter%5Bid%5D'

完成。就是这样。没什么。

我不得不说,当我试图让你的代码运行时,它充满了错误和问题。我怀疑您发布的代码不是您的生产代码,而是您专门针对此问题编写的示例。我建议您在发布之前尝试制作一个可运行的可编辑示例。

然而,我的建议应该适合你一点点调整。

它是多线程和线程安全的。完成后它确实干净地保存修改后的集合。