C# 批处理ConcurrentBag中的所有项目
我有以下用例。多个线程正在创建在ConcurrentBag中收集的数据点。每x毫秒,一个使用者线程会查看自上次以来传入的数据点并对其进行处理(例如,计数+计算平均值) 以下代码或多或少代表了我提出的解决方案:C# 批处理ConcurrentBag中的所有项目,c#,concurrency,C#,Concurrency,我有以下用例。多个线程正在创建在ConcurrentBag中收集的数据点。每x毫秒,一个使用者线程会查看自上次以来传入的数据点并对其进行处理(例如,计数+计算平均值) 以下代码或多或少代表了我提出的解决方案: private static ConcurrentBag<long> _bag = new ConcurrentBag<long>(); static void Main() { Task.Run(() => Consume()); var
private static ConcurrentBag<long> _bag = new ConcurrentBag<long>();
static void Main()
{
Task.Run(() => Consume());
var producerTasks = Enumerable.Range(0, 8).Select(i => Task.Run(() => Produce()));
Task.WaitAll(producerTasks.ToArray());
}
private static void Produce()
{
for (int i = 0; i < 100000000; i++)
{
_bag.Add(i);
}
}
private static void Consume()
{
while (true)
{
var oldBag = _bag;
_bag = new ConcurrentBag<long>();
var average = oldBag.DefaultIfEmpty().Average();
var count = oldBag.Count;
Console.WriteLine($"Avg = {average}, Count = {count}");
// Wait x ms
}
}
private static ConcurrentBag\u bag=new ConcurrentBag();
静态void Main()
{
Task.Run(()=>Consume());
var producerTasks=Enumerable.Range(0,8)。选择(i=>Task.Run(()=>product());
Task.WaitAll(producertask.ToArray());
}
私有静态void product()
{
对于(int i=0;i<100000000;i++)
{
_添加(i);
}
}
私有静态void消费()
{
while(true)
{
var oldBag=_bag;
_bag=新的ConcurrentBag();
var average=oldBag.DefaultIfEmpty().average();
var count=oldBag.count;
WriteLine($“Avg={average},Count={Count}”);
//等待x毫秒
}
}
- ConcurrentBag是适合这项工作的工具吗李>
- 交换行李是否是实现清除新数据点列表,然后处理旧数据点的正确方法
- 在oldBag上操作安全吗?或者当我在oldBag上迭代时,线程仍在添加项目,我会遇到麻烦吗
- 我应该使用Interlocked.Exchange()来切换变量吗
public class LogCollectorTarget : TargetWithLayout, ILogCollector
{
private readonly List<string> _logMessageBuffer;
public LogCollectorTarget()
{
_logMessageBuffer = new List<string>();
}
protected override void Write(LogEventInfo logEvent)
{
var logMessage = Layout.Render(logEvent);
lock (_logMessageBuffer)
{
_logMessageBuffer.Add(logMessage);
}
}
public string GetBuffer()
{
lock (_logMessageBuffer)
{
var messages = string.Join(Environment.NewLine, _logMessageBuffer);
_logMessageBuffer.Clear();
return messages;
}
}
}
公共类日志采集器目标:带有布局的目标,ILogCollector
{
私有只读列表_logMessageBuffer;
公共日志收集器目标()
{
_logMessageBuffer=新列表();
}
受保护的重写无效写入(LogEventInfo logEvent)
{
var logMessage=Layout.Render(logEvent);
锁定(_logMessageBuffer)
{
_logMessageBuffer.Add(logMessage);
}
}
公共字符串GetBuffer()
{
锁定(_logMessageBuffer)
{
var messages=string.Join(Environment.NewLine,_logMessageBuffer);
_logMessageBuffer.Clear();
返回消息;
}
}
}
该类的用途是收集日志,以便可以将它们批量发送到服务器。每x秒调用一次GetBuffer。这将获取当前日志消息并清除新消息的缓冲区。它与锁一起工作,但由于它们非常昂贵,我不想锁定程序中的每个日志操作。这就是为什么我想使用ConcurrentBag作为缓冲区。但在调用GetBuffer时,我仍然需要切换或清除它,而不会丢失切换期间发生的任何日志消息
ConcurrentBag是适合这项工作的工具吗
这是一项工作的正确工具,这实际上取决于你想做什么,以及为什么。您给出的示例非常简单,没有任何上下文,因此很难说
交换行李是否是正确的方式,以实现清除行李清单
新数据点,然后处理旧数据点
答案是否定的,可能有很多原因。当您切换线程时,如果线程写入它会发生什么情况
在oldBag上操作安全吗?或者当我
在oldBag上迭代,线程仍在添加项目
不,您只是复制了引用,这将一事无成
我应该使用Interlocked.Exchange()来切换变量吗
互锁方法非常有用,但是这对解决当前的问题没有帮助,它们用于线程安全地访问整型值。您真的很困惑,需要查找更多线程安全示例
但是,让我们为您指出正确的方向。忘了ConcurrentBag和那些花哨的课程吧。我的建议是从简单开始并使用锁定,这样您就可以了解问题的本质 如果希望多个任务/线程访问列表,可以轻松使用
lock
语句保护对列表/数组的访问,这样其他讨厌的线程就不会修改它
显然,您编写的代码是一个毫无意义的示例,我的意思是,您只是将连续的数字添加到列表中,然后让另一个线程对它们进行平均。这几乎不需要是消费者生产者,而且如果是同步的,就更有意义了
在这一点上,我将向您介绍更好的体系结构,使您能够实现这种模式,例如Tpl数据流,但我担心这只是一种学习练习,不幸的是,在我们真正帮助您解决问题之前,您确实需要更多地阅读多线程并尝试更多的示例 由于您只有一个消费者,您可以使用简单的ConcurrentQueue,而无需交换集合:
public class LogCollectorTarget : TargetWithLayout, ILogCollector
{
private readonly ConcurrentQueue<string> _logMessageBuffer;
public LogCollectorTarget()
{
_logMessageBuffer = new ConcurrentQueue<string>();
}
protected override void Write(LogEventInfo logEvent)
{
var logMessage = Layout.Render(logEvent);
_logMessageBuffer.Enqueue(logMessage);
}
public string GetBuffer()
{
// How many messages should we dequeue?
var count = _logMessageBuffer.Count;
var messages = new StringBuilder();
while (count > 0 && _logMessageBuffer.TryDequeue(out var message))
{
messages.AppendLine(message);
count--;
}
return messages.ToString();
}
}
谢谢你的评论。上面的例子不能很好地说明这个问题。我编辑了这个问题谢谢,我希望有一个“出列所有”的方法,但那应该行得通!
public string GetBuffer()
{
// How many messages should we dequeue?
var count = _logMessageBuffer.Count;
var buffer = new string[count];
for (int i = 0; i < count; i++)
{
_logMessageBuffer.TryDequeue(out var message);
buffer[i] = message;
}
return string.Join(Environment.NewLine, buffer);
}