C# 如何使用TPL管道从多个线程安全地访问集合

C# 如何使用TPL管道从多个线程安全地访问集合,c#,task-parallel-library,C#,Task Parallel Library,下面是使用TPL数据流的非常简化的代码示例: //This is collection where I register all items that need to be processed by pipeline //MyData is simple class with 2 properties = int Id, bool IsCompleted private ConcurrentBag<MyData> completedItems = new Concurr

下面是使用TPL数据流的非常简化的代码示例:

//This is collection where I register all items that need to be processed by pipeline
//MyData is simple class with 2 properties = int Id, bool IsCompleted       
private ConcurrentBag<MyData> completedItems = new ConcurrentBag<MyData>();
//This method may be called multiple times in a very short time frame which means that
//few pipelines may be running simultaneously.
public void InitiateProcess(List<MyData> inputData)
{
    inputData.ForEach(ent => completedItems.Add(ent));
    StartPipeline(inputData);
}
public void StartPipeline(List<MyData> inputData)
{
    //Here goes TransformBlock downloadBlock=...
    //Here goes TransfromBlock processBlock=...
    //In the resultBlock I would like to update corresponding item in completedItems bag.
    var resultBlock = new ActionBlock<MyData>(data =>
    {
        var completedItem = completedItems.FirstOrDefault(ent => ent.Id == data.Id);
        if (completedItem != null)
            completedItem.IsCompleted = true;
    });
}
//这是一个集合,我在其中注册所有需要通过管道处理的项
//MyData是一个简单的类,有两个属性=int Id,bool IsCompleted
私有ConcurrentBag completedItems=新ConcurrentBag();
//此方法可以在很短的时间内多次调用,这意味着
//少数管道可能同时运行。
public void InitiateProcess(列表输入数据)
{
inputData.ForEach(ent=>completedItems.Add(ent));
起始管线(输入数据);
}
公用无效起始管线(列出输入数据)
{
//下面是TransformBlock downloadBlock=。。。
//下面是TransfromBlock processBlock=。。。
//在resultBlock中,我想更新completedItems包中的相应项目。
var resultBlock=新操作块(数据=>
{
var completedItem=completedItems.FirstOrDefault(ent=>ent.Id==data.Id);
if(completedItem!=null)
completedItem.IsCompleted=true;
});
}
我的主要目标是注册已成功完成的项目。没有什么很复杂的,但是我对并行编程了解得越多,我就越了解它有多复杂,使用它时应该非常小心。我知道可能有多个不同的线程试图同时访问
completedItems
集合。我做了一些研究,使用
ConcurrentBag
跟踪这些项目似乎是一个不错的方法。所以我的问题是,使用这种方法是否存在潜在的危险

根据微软

ConcurrentBag的所有公共和受保护成员都是线程安全的,可以从多个线程并发使用

这意味着
ConcurrentBag
被设计为可由多个线程访问,而不必担心它们相互干扰

编辑:我刚刚注意到您使用的是
FirstOrDefault
,同样根据文档使用

但是,通过ConcurrentBag实现的接口之一(包括扩展方法)访问的成员不能保证是线程安全的,可能需要调用方进行同步


因此,
FirstOrDefault
可能不是线程安全的,而
ConcurrentBag
允许存在重复项。也许切换到
ConcurrentDictionary
或使用带有
lock
语句的
列表会更好

谢谢您的回答!是的,你说得对。我最好切换到
ConcurrentDictionary
,然后使用
TryUpdate
访问和更新成员!