1

upd:私の質問を簡単に言い換えさせてください。N 個の二重数があります。N 個の専用スレッドがあり、それぞれが独自の倍精度数を更新します (_cachedProduct以下の例)。

どういうわけかsum、これらの数値を取得する必要がありIndexUpdated、2 倍の数値が変更された後、できるだけ早くイベントを発生させる必要があります (そのようなイベントを 10 μs 以下で発生させることができればよいでしょう)。

以下は、このタスクを実装しようとした方法です

===============================================

証券取引所指数を計算するには、private double[] _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];
        }
    }

_ammountsは事前に初期化された配列であり、Initializeメソッドと変数を無視してください - それらは機能します。

ループでは、すべての _cachedProduct を合計し、値が変更されたときに他の人に通知します。

        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;
    }

Interlockeddouble 値を更新するために使用する必要があり_cachedProduct ますが、今はその事実を無視してください。このコードには他にどのような問題がありますか?

Calculate内部でメソッドを呼び出す必要がwhile(true)あるので、always遅延なく 1 つのコアを使用します。私のマシンには24コアがあるので、これでいいと思っていました。

ただし、Thread.Sleep(5)(コメントなしで)プログラム全体で大幅な速度低下が見られますが、その理由はわかりません。プログラムは、多くの場所で数十倍遅く実行されます。

while(true)問題は、ロックをまったく使用せずに使用するという私の考えが正しいかどうかです。または、ロック方法を導入して、いずれかが更新されCalculateたときにのみインデックスを作成する必要がありますか?_cachedProduct

4

3 に答える 3

1

合計に余分なスレッドとループを使用しないと、パフォーマンスが向上し、コードがより明確になる可能性があると思います。機器に変更を加えるたびに、差額を計算し、すぐにインデックスを更新して通知を実行します

したがって、スレッドが単一の機器に対してInstrumentUpdatedを呼び出す場合。

  change = newvalue - currentvalue;
  // used interlocked here to change the index threadsafe
  StockExchangeSum = Interlocked.Add(ref StockExchangeSum,change);
  NotifyIndexChanged();
于 2012-07-09T10:06:28.297 に答える
0

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();
    }
});
于 2012-07-09T11:56:52.057 に答える
0

あなたが考えるいくつかのポイント

  • コードの残りの部分とは別に、計算ブロックのプロファイリングを試みましたか? 私はあなたのCalculate関数でこれに気づきました:

    for (int i = 0; i < _instrumentIds.Count(); i++)

    _instrumentIds.Count() は、コレクション全体に対して反復を呼び出します。これは、ループの各トリップに対して呼び出される可能性があります。つまり、_instrumentIds の N^2/2 回の反復を行っています。

  • この計算操作中に _instrumentIdsIEnumerable が変更されていますか? もしそうなら、あなたは間違った答えにつながるあらゆる種類の競合状態を得ることができます.

  • CalculateAndNotify を含むタスクは 1 回呼び出されますか、それとも何度も呼び出されますか (ネストされていますか)? たとえば、CalculateAndNotify 内に、再帰的にトリガーされる操作がありますか?

    その場合、複数の計算が同時に実行されていることがわかります (プールが枯渇するまで複数のスレッドを使用します)。操作の開始/終了時にログを記録し、おそらく同時計算の数を数えてこれを確認できますか?

    これが問題である場合は、CalculateAndNotify 操作がキューに入れられ、前の操作が完了するまで新しい計算操作を実行できないロジックを含めることができます。

于 2012-07-09T13:17:19.070 に答える