使用多线程时如何添加到列表?

Posted

技术标签:

【中文标题】使用多线程时如何添加到列表?【英文标题】:How to add to a List while using Multi-Threading? 【发布时间】:2012-05-24 02:54:25 【问题描述】:

我对多线程有点陌生,过去只玩过它。但我很好奇是否有可能在主线程上有一个字节数组列表,并且在单独的线程中创建新的字节数组时仍然能够添加到该列表中。此外,我将使用一个 for-each 循环,该循环将遍历将用于解析为字节数组的表单列表。所以基本上一个伪代码会是这样的......

reports = new List();

foreach (form in forms)  
  
    newReport = new Thread(ParseForm(form));  
    reports.Add(newReport);  

  

void ParseForm(form)  
  
    newArray = new byte[];  
    newArray = Convert.ToBytes(form);  
    return newArray;  

希望上面的伪代码有些意义。如果有人能告诉我这是否可能,并指出一个好的例子的方向,我相信我能找出实际的代码。

【问题讨论】:

忘了提,网络服务使用的是 .Net 3.5,所以我不相信任务可用。 【参考方案1】:

如果您需要从多个线程访问一个集合,您应该使用同步,或者如果您的 .NET 版本是 3.0 或更高版本,则使用 SynchronizedCollection

这是使您的线程可以访问该集合的一种方法:

SynchronizedCollection reports = new SynchronizedCollection();

foreach (form in forms)   
    var reportThread = new Thread(() => ParseForm(form, reports));
    reportThread.Start();


void ParseForm(Form form, SynchronizedCollection reports)   
    newArray = new byte[];  
    newArray = Convert.ToBytes(form);  
    reports.Add(newArray);

如果您使用的是 .NET 4 或更高版本,System.Threading.Tasks 命名空间的各种类提供了一种比手动管理线程更好的替代方法。在决定您的线程实现之前,请考虑探索此替代方案。

【讨论】:

谢谢,我得试试 SynchronizedCollection,因为我还在 3.5 上。 我需要为 SynchronizedCollection 设置引用吗?我有 Collections.Genereric 和 Threading 的 using 语句。我检查它使用的是 3.5,但我没有得到 SynchronizedCollection。 @jhorton 你需要添加对System.ServiceModel的引用。 谢谢,让它重新识别。让我看看这是否可行。【参考方案2】:

在我们意识到它是 .Net 3.5 之前,请保留在 .Net 4 上以供参考

如果您不需要列表中的任何顺序,一个简单的“修复”是使用ConcurrentBag<T> 类而不是列表。如果您需要更多订单,还有一个ConcurrentQueue<T> 集合。

如果你真的需要更多自定义的东西,你可以使用 BlockingCollection 实现你自己的阻塞集合。 Here's a good article 正题。

您也可以使用 Parallel.Foreach 来避免显式创建线程:

private void ParseForms()

    var reports = new ConcurrentBag<byte[]>();
    Parallel.ForEach(forms, (form) =>
                                
                                    reports.Add(ParseForm(form));
                                );



private byte[] ParseForm(form)  
  
    newArray = new byte[];  
    newArray = Convert.ToBytes(form);  
    return newArray;  

【讨论】:

ConcurrentBag 是否只有 4.0 或更高版本? 是的,我也看到了你的笔记。如果可以使用,这在 .Net 4.0 中非常简单。 我必须同意你的观点。我不确定他们为什么还没有将此服务升级到 4.0。其他一切都在使用它。 否则您将不得不使用线程池并编写自己的阻塞集合。升级到 .Net 4.0 并使用 Task Parallel Libraries 是完全值得的,因为对于我们这些单线程凡人来说,很难让它正确。【参考方案3】:

Why is enumerate files returning the same file more than once?

检查一下。它表明我认为你想做什么。

它在主线程上创建一个列表,然后从另一个线程添加到它。 你需要

using System.Threading.Tasks

-

Files.Clear(); //List<string> 

Task.Factory.StartNew( () =>

      this.BeginInvoke( new Action(() =>
         
            Files.Add("Hi");
         ));
 );

【讨论】:

仅当 this 是 System.Windows.Forms.Control(或派生类)时才有效。它不是一种同步机制,而是一种从其他线程与 UI 交互的方式。【参考方案4】:

下面是一个简单的阻塞集合(仅作为队列),因为您无权访问 C# 4.0,所以我现在刚刚创建了它。它很可能比 4.0 并发集合效率低,但它应该工作得很好。我没有重新实现所有的队列方法,只是入队、出队和窥视。如果您需要其他人但不知道如何实施,只需在 cmets 中提及即可。

一旦你有了工作的阻塞集合,你就可以简单地从生产者线程添加到它,并使用消费者线程从中删除。

public class MyBlockingQueue<T>

    private Queue<T> queue = new Queue<T>();
    private AutoResetEvent signal = new AutoResetEvent(false);
    private object padLock = new object();

    public void Enqueue(T item)
    
        lock (padLock)
        
            queue.Enqueue(item);
            signal.Set();
        
    

    public T Peek()
    
        lock (padLock)
        
            while (queue.Count < 1)
            
                signal.WaitOne();
            

            return queue.Peek();
        
    

    public T Dequeue()
    
        lock (padLock)
        
            while (queue.Count < 1)
            
                signal.WaitOne();
            

            return queue.Dequeue();
        
    

【讨论】:

以上是关于使用多线程时如何添加到列表?的主要内容,如果未能解决你的问题,请参考以下文章

C# - 锁定和 StreamWrite 问题(多线程)

Python中的多处理:处理多个工作线程

C# Timer 检查多线程是不是完成失败

多线程环境下需要 ArrayList 的防呆同步

python读取大文件处理时使用多线程

[备忘]不用许可证 多线程直接操作界面组件比如超级列表框的实现