C# 无锁计算:如何对其他线程正在更改的N个双倍数求和?
upd:让我简单地重新表述一下我的问题。 有N个双数字。有N个专用线程,每个线程更新自己的双倍编号(C# 无锁计算:如何对其他线程正在更改的N个双倍数求和?,c#,performance,locking,C#,Performance,Locking,upd:让我简单地重新表述一下我的问题。 有N个双数字。有N个专用线程,每个线程更新自己的双倍编号(\u cachedProduct,在下面的示例中) 不知何故,我需要对这些数字进行求和,并且我需要在更改任何双倍数字后尽快引发索引更新事件(如果可以在10µs或更短的时间内引发此类事件,那就太好了) 下面是我如何尝试执行此任务的 =============================================== 为了计算股票交易所指数,我创建了private double[]\u ca
\u cachedProduct
,在下面的示例中)
不知何故,我需要对这些数字进行求和
,并且我需要在更改任何双倍数字后尽快引发索引更新
事件(如果可以在10µs或更短的时间内引发此类事件,那就太好了)
下面是我如何尝试执行此任务的
===============================================
为了计算股票交易所指数,我创建了private double[]\u cachedProduct代码>字段。这些字段已写入
千丝万缕
// called from another threads
public override void InstrumentUpdated(Instrument instrument)
{
if (!_initialized)
{
if (!Initialize())
{
return;
}
}
int instrumentId = instrument.Id;
OrderBook ob = Program.market.OrderBook(instrument);
if (ob.MedianOrAskOrBid == null)
{
_cachedProduct[instrumentId] = 0;
}
else
{
_cachedProduct[instrumentId] = ((double) ob.MedianOrAskOrBid)*_ammounts[instrumentId];
}
}
\u amounts
是预先初始化的数组,请忽略初始化
方法和变量-它们只起作用
在循环中,我只对所有缓存产品求和,当值更改时,我会通知其他人
Task.Factory.StartNew(() =>
{
while(true)
{
if (_initialized)
{
break;
}
}
while (true)
{
CalculateAndNotify();
//Thread.Sleep(5);
}
}
, TaskCreationOptions.LongRunning);
protected void CalculateAndNotify()
{
var oldValue = Value;
Calculate();
if (oldValue != Value)
{
NotifyIndexChanged();
}
}
protected override void Calculate()
{
double result = 0;
for (int i = 0; i < _instrumentIds.Count(); i++)
{
int instrumentId = _instrumentIds[i];
if (_cachedProduct[instrumentId] == 0)
{
Value = null;
return;
}
result += _cachedProduct[instrumentId];;
}
Value = result;
}
Task.Factory.StartNew(()=>
{
while(true)
{
如果(_已初始化)
{
打破
}
}
while(true)
{
CalculateAndNotify();
//睡眠(5);
}
}
,TaskCreationOptions.LongRunning);
受保护的void CalculateAndNotify()
{
var oldValue=价值;
计算();
如果(旧值!=值)
{
NotifyIndexChanged();
}
}
受保护的覆盖无效计算()
{
双结果=0;
对于(int i=0;i<_instrumentIds.Count();i++)
{
int-instrumentId=_-instrumentId[i];
if(_cachedProduct[instrumentId]==0)
{
值=空;
返回;
}
结果+=_cachedProduct[instrumentId];;
}
值=结果;
}
我必须使用Interlocked
来更新我的double\u cachedProduct
值,但现在请忽略这一事实,您看到此代码还有哪些其他问题
我是否应该在中调用Calculate
方法,而(true)
因此我总是使用一个内核而不延迟。我的机器有24个核心,所以我认为这是可以的
然而,如果没有Thread.Sleep(5)
(注释),我确实看到整个程序的速度明显减慢,我不明白为什么。程序在许多地方的执行速度要慢几十倍
问题是我使用while(true)
而不使用任何锁定的想法是否可行。或者我应该引入一些锁定方法,以便在更新\u cachedProduct
中的一个时,我只计算索引?我认为如果不使用额外的线程和循环进行求和,您可能会获得更好的性能和更清晰的代码。对仪器进行每次更改时,您都会计算差异,并立即更新索引并执行通知
因此,如果线程为单个工具调用InstrumentUpdated
change = newvalue - currentvalue;
// used interlocked here to change the index threadsafe
StockExchangeSum = Interlocked.Add(ref StockExchangeSum,change);
NotifyIndexChanged();
我认为,如果不使用额外的线程和循环进行求和,您可能会获得更好的性能和更清晰的代码。对仪器进行每次更改时,您都会计算差异,并立即更新索引并执行通知
因此,如果线程为单个工具调用InstrumentUpdated
change = newvalue - currentvalue;
// used interlocked here to change the index threadsafe
StockExchangeSum = Interlocked.Add(ref StockExchangeSum,change);
NotifyIndexChanged();
double[]可以是更复杂的类型吗?
WaitHandle.WaitAny在性能方面如何比较
大致如下
private Index[] indicies;
public class Index
{
public WaitHandle Updated =
new EventWaitHandle(false, EventResetMode.AutoReset);
public double _value;
public double Value
{
get {return _value;}
set
{
if(_value != value)
{
_value = value;
Updated.Set();
}
}
}
}
TaskFactory.StartNew(() =>
{
while(true)
{
WaitHandle.Any(indicies.Select(i => i.Updated));
CalculateAndNotify();
}
});
double[]可以是更复杂的类型吗?
WaitHandle.WaitAny在性能方面如何比较
大致如下
private Index[] indicies;
public class Index
{
public WaitHandle Updated =
new EventWaitHandle(false, EventResetMode.AutoReset);
public double _value;
public double Value
{
get {return _value;}
set
{
if(_value != value)
{
_value = value;
Updated.Set();
}
}
}
}
TaskFactory.StartNew(() =>
{
while(true)
{
WaitHandle.Any(indicies.Select(i => i.Updated));
CalculateAndNotify();
}
});
你需要考虑的几点
- 您是否尝试过独立于代码的其余部分分析计算块?我在计算函数中注意到:
对于(int i=0;i<_instrumentIds.Count();i++)
_Count()调用整个集合上的迭代,并且有可能在循环的每次行程中都调用该迭代。i、 e.您正在进行N^2/2次的_InstrumentID迭代
- 在此计算操作期间是否修改了_instrumentIdsIEnumerable?如果是这样的话,你可能会得到各种各样的比赛条件,导致错误的答案
- 包含CalculateAndNotify的任务是一次调用还是多次调用(嵌套)?例如,CalculateAndNotify中是否存在可能导致递归触发的操作
如果是这样,您可能会发现同时执行了多个计算(使用多个线程,直到池耗尽)。您是否可以在操作的开始/结束时记录一些日志,或者计算同时计算的次数以检查这一点
如果这是一个问题,您可以包括一些逻辑,其中CalculateAndNotify操作排队,新的计算操作在前一个操作完成之前无法执行
您需要考虑的一些要点
- 您是否尝试过独立于代码的其余部分分析计算块?我在计算函数中注意到:
对于(int i=0;i<_instrumentIds.Count();i++)
_Count()调用整个集合上的迭代,并且有可能在循环的每次行程中都调用该迭代。i、 e.您正在进行N^2/2次的_InstrumentID迭代
- 在此计算操作期间是否修改了_instrumentIdsIEnumerable?如果是这样的话,你可能会得到各种各样的比赛条件,导致错误的答案
- 包含CalculateAndNotify的任务是一次调用还是多次调用(嵌套)?例如,CalculateAndNotify中是否存在可能导致递归触发的操作
如果是这样,你可能会发现你有几个计算