基本上我需要的是能够不断地向List(或其他集合)添加项目,在一个线程中每秒大约3000次。并且每2秒钟从该列表中获取和删除所有项目。
我不喜欢经典的方法,比如使用并发集合或每次我需要访问集合时锁定某些内容,因为它会比我需要的更慢。
我要做的是拥有2个集合,每个线程一个,并找到一种方法使线程安全地从一个集合切换到另一个集合。
简化而非线程安全示例:
var listA = new List<int>();
var listB = new List<int>();
// method is called externally 3000 times per second
void ProducerThread(int a)
{
listA.Add(a)
}
void ConsumerThread()
{
while(true)
{
Thread.Sleep(2000);
listB = Interlocked.Exchange(ref listA,listB);
//... processing listB data
// at this point when i'm done reading data
// producer stil may add an item because ListA.Add is not atomic
// correct me if i'm wrong
listB.Clear();
}
}
有没有办法让上面的代码按预期工作(线程安全),同时尽可能少地阻止生产者线程?或者可能是另一种解决方案?
答案 0 :(得分:2)
我首先在System.Collections.Concurrent中使用BlockingCollection
或其他IProducerConsomerCollection
开始。这正是您所拥有的,从多个线程访问的生产者/消费者队列。这些系列还针对性能进行了大量优化。他们不使用天真的“任何人做任何操作时锁定整个结构”。它们足够聪明,可以使用无锁同步技术尽可能地避免锁定,并且当它们确实需要使用关键部分时,它们可以最小化需要锁定的内容,以便尽管存在一定量的锁定,通常可以同时访问该结构。
在我从那里移动到其他任何东西之前,我会使用其中一个集合并确保它太慢。如果在使用它作为您的解决方案后,您已经证明您花费了不可接受的时间来添加/删除集合中的项目,那么您可以考虑调查其他解决方案。
如果我怀疑是这样的话,他们的表现足够快,我相信你会发现这使得编写代码更多更容易阅读。
答案 1 :(得分:1)
我假设您只想处理listA
的新添加内容,并且在处理这些添加内容时会进行更多添加。
var listA = new List<int>();
var dictA = new Dictionary<int,int>();
int rangeStart = 0;
int rangeEnd = 0;
bool protectRange = false;
// method is called externally 3000 times per second
void ProducerThread(int a)
{
listA.Add(a);
dictA.Add(rangeEnd++,a);
}
void ConsumerThread()
{
while(true)
{
Thread.Sleep(2000);
int rangeInstance = rangeEnd;
var listB = new List<int>();
for( int start = rangeStart; start < rangeInstance; start++ ){
listB.add(dictA[start]);
rangeStart++;
}
//... processing listB data
}
}
答案 2 :(得分:0)
如果表格有最大固定大小,为什么要使用List?您也可以预先设置列表大小。
List<int> listA = new List<int>(6000);
现在,我还没有真正测试过以下内容,但我认为它会做你想做的事情:
int[] listA = new int[6000]; // 3000 time * 2 seconds
int i = 0;
// method is called externally 3000 times per second
void ProducerThread(int a)
{
if (Monitor.TryEnter(listA)) // If true, consumer is in cooldown.
{
listA[i] = a;
i++;
Monitor.Exit(listA);
}
}
void ConsumerThread()
{
Monitor.Enter(listA); // Acquire thread lock.
while (true)
{
Monitor.Wait(listA, 2000); // Release thread lock for 2000ms, automaticly retake it after Producer released it.
foreach (int a in listA) { } //Processing...
listA = new int[6000];
i = 0;
}
}
你只需要确保首先运行ConsumerThread,这样它就会排队并等待。