0

基本上我需要的是能够不断地将项目添加到列表(或另一个集合)中,在一个线程中每秒大约 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();
  }
}

有什么方法可以使上述代码按预期工作(线程安全),同时尽可能少地阻塞生产者线程?或者也许是另一种解决方案?

4

3 回答 3

2

我将从在 System.Collections.Concurrent 中使用一个BlockingCollection或另一个开始。IProducerConsomerCollection这正是你所拥有的,一个从多个线程访问的生产者/消费者队列。这些集合也针对性能进行了大量优化。他们不会使用幼稚的“在任何人进行任何操作时锁定整个结构”。它们足够聪明,可以使用无锁同步技术尽可能避免锁定,并且当它们确实需要使用临界区时,它们可以最大限度地减少需要锁定的部分,以便尽管有一定数量的锁定,但通常可以同时访问该结构。

在我从那里转移到其他任何东西之前,我会使用其中一个集合并确保它太慢。如果在使用它作为您的解决方案之后,您已经证明您花费了不可接受的时间从集合中添加/删除项目,那么您可以考虑调查其他解决方案。

如果,正如我所怀疑的那样,它们执行得足够快,我相信您会发现它使编写代码变得更容易和更清晰易读。

于 2012-10-16T17:38:29.907 回答
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
  }
}
于 2012-10-16T17:56:50.940 回答
0

如果表有固定的最大大小,为什么要使用列表?您还可以预先设置列表大小。

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,这样它就会自己排队等待。

于 2012-10-16T17:25:20.333 回答