使用多线程时如何添加到列表?
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
您也可以使用 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();
【讨论】:
以上是关于使用多线程时如何添加到列表?的主要内容,如果未能解决你的问题,请参考以下文章