3

イベントを発生させるクラスを作成したいと思います。xこれは、1 秒間に事前に定義された回数呼び出されnます。

ただし、毎秒均等に発砲したくx ありません。

したがって、たとえばn= 100 とすると、最初の 300 ミリ秒で 25 発、次の 600 ミリ秒でさらに 50 発、残りの 100 ミリ秒で最後の 25 発が発生する可能性があります。

理想的には、上記よりも優れた粒度と、1 秒あたりの間隔の範囲を広げたいと考えています。

定義されたミリ秒と発生するイベントの数で配列を作成できるかどうか疑問に思っています。次に、ループとStopwatchクラスを使用して、そのミリ秒のウェイポイントに対して定義されたイベントを発生させる必要があるかどうかを判断します。

問題は、1 秒ごとに十分な速さで配列を計算できるかどうかです。1 秒ごとにランダムな間隔が必要です。

明らかに、イベントに接続されているものによって遅延が発生するのを避けるために、イベントは非同期である必要があります。

誰もが同様のニーズに出くわしますか?

アップデート

少なくともここで最初の努力をしようと思いました。

したがって、現在のミリ秒と秒を確認するのに十分な解像度があることがわかりました。基本的に、1 秒ごとに間隔を再構築し、ミリ秒ごとに配列内のエントリを取得し、各エントリはそのミリ秒でイベントを発生させる回数を示します。

私の問題は間隔です...イベントカウントを試してまとめるには、より良い方法が必要です.現在、このjsutは、0、1、または2のいずれかに均等に配置されているようです.

public delegate void EmptyEventDelegate();
    public class RandEvent
    {
        public event EmptyEventDelegate OnEvent = delegate { };
        private bool running = false;
        Random r = new Random();

        private int eventsPS;

        public RandEvent(int eventsPS = 1)
        {
            this.eventsPS = eventsPS;
        }

        public void Start()
        {
            running = true;
            Task.Factory.StartNew(() =>
                {
                    Run();
                });
        }

        private void Run()
        {
            var sw = new Stopwatch();
            sw.Start();

            int currentSecond = 0;
            int[] eventCount = BuildEventSpacing();

            while(running)
            {
                if (currentSecond != sw.Elapsed.Seconds)
                {
                    currentSecond = sw.Elapsed.Seconds;
                    eventCount = BuildEventSpacing();
                }
                else
                {
                    for(int i = 0; i < eventCount[sw.Elapsed.Milliseconds]; i++)
                        OnEvent();

                }
            }

            sw.Stop();
        }

        private int[] BuildEventSpacing()
        {
            var array = new int[1000];

            for (int i = 0; i < eventsPS; i++)
            {
                array[r.Next(0, 999)]++;
            }

            return array;
        }

        public void Stop()
        {
            running = false;
        }

    }
4

5 に答える 5

0

TimeSpanオブジェクトのコレクションを等しくなるように初期化し、合計が1秒になるようにすることをお勧めします。正確な値は、によって決定されますn

そこから、ペアまたはグループを任意の値でオフセットでき、合計は1秒のままになります。例えば...

var offset = new TimeSpan.FromMilliseconds(10); // 10ms offset
timeSpans[0] += offset;
timeSpans[1] -= offset;

このようにして、全体の合計に影響を与えることなく、それらをシフトすることができます。より多くが早く来て、より少なく来るようにそれらを作る必要があるなら、あなたは次のようなことをすることができます...

timeSpans[0] -= offset;
timeSpans[1] -= offset;
timeSpans[2] += offset;
timeSpans[2] += offset;

これにより、インデックス0と1の遅延が短くなり、インデックス2の遅延が2倍になりますが、合計は影響を受けません。

覚えておくべき唯一のことは、どの期間も0未満であってはならず、合計は常に1でなければならないということです。そうすれば、あなたは黄金になります。TimeSpanオブジェクトを配布したら、それらを使用して、イベントの発生の合間に一時停止できますx。オフセットを数回変更(ランダム化?)し、変更するたびに、オフセットを適用する新しいtimeSpans(ランダム?)を選択します。プロセスの最後には、かなり無秩序に間隔を空ける必要があります。

これが抽象的すぎる場合は、より詳細なコードサンプルを提供できます。:)

お役に立てれば!

于 2012-08-30T18:37:15.177 に答える
0

合計が 1 である 100 個の数値が必要だとします。次のように実行できます。

  1. 0 から 1 までの乱数 (つまりRandom.NextDouble) を 100 個生成し、リストに格納します。
  2. 数字を合計します。
  3. リスト内の各数値を合計で割ります。

これで、合計が 1.0 になる 100 個の数値のリストができました。

ここで、間隔がリストの最初の値であるワンショット タイマーを作成します。起動すると、ハンドラーはそのジョブを実行し、リスト内の次の値に等しい間隔でワンショット タイマーを再度設定します。

ただし、ティックを処理してタイマーを再設定するのに必要な時間を考慮していないため、完全には機能しません。したがって、そのレイテンシを追跡し、それに応じて次のティックを調整する必要があります。しかし、それでも完璧にはなりません。

コメントで述べたように、どの .NET タイマーでも 1 秒あたり 60 ティック以上を取得するのは難しいでしょう。せいぜい、タイマー刻みの間に 15 ミリ秒を期待できます。カスタム タイマーの実装を行ったとしても、タイマー ティックが正確に時間通りに発生しないため、問題が発生することになります。もちろん、タイマー間隔が短くなるにつれて、問題は悪化します。

いずれにせよ、Windows タイマーで 1 ミリ秒以上の精度を得る方法はないため、上記の方法を調整して 0.001 以上の数値を取得する必要があります。

を使用するというあなたのアイデアはStopwatch、イベント間隔を作成する私の方法で機能します。ただし、ループは 1 つのコアで 100% 近くの CPU 時間を消費することを理解しておいてください。それでも、他の優先度の高いタスクによってループがスワップアウトされる可能性があるため、イベントが正確に時間通りに提供されない可能性があります。

于 2012-08-30T18:46:37.620 に答える
0

ランダムなイベント時間の配列を作成するには:

  1. イベント間の最小時間間隔を選択し、可能なすべてのイベント時間を配列に入力します。たとえば、1 ミリ秒の粒度を選択すると、配列には 0、0.001、0.002、...、0.999 の 1000 個の値が含まれます。
  2. 1 秒ごとに配列をシャッフルして、要素の順序をランダム化します。
  3. 配列の最初の n 個の要素をイベントの発生時間として使用します。
于 2012-08-30T18:00:37.737 に答える
0

を作成Timerし、Tickイベントで必要なことは何でも実行します。さらに、次のメソッドを (イベントの追加ハンドラーでTick) 使用して、間隔を毎回変更します。Random新しいインスタンスを提供するのではなく、毎回同じインスタンスをメソッドに渡すことをお勧めします。

private static double millisecondsPerSecond = 1000.0;
/// <summary>
/// Method used to determine how long you would wait for the event to fire next
/// </summary>
/// <param name="averageFiresPerSecond">The approximate number of times the event should occur per second.</param>
/// <param name="variance">How much variance should be allowed, as a percentage.  i.e. a variance of 0.1 would mean that 
/// the delay will be +/- 10% of the exact rate.</param>
/// <param name="generator">A randon number generator.</param>
/// <returns>The number of milliseconds to wait for the next event to fire.</returns>
public double GetNextDelay(int averageFiresPerSecond, double variance, Random generator)
{
    double randomFactor = ((generator.NextDouble() * 2) * variance);
    return (millisecondsPerSecond / averageFiresPerSecond) * randomFactor;
}
于 2012-08-30T18:15:56.310 に答える
0
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;

namespace TestProject1
{
    [TestClass]
    public class Test
    {
        [TestMethod]
        public void TestSteps()
        {   
            var random = new Random();
            for (int i = 0; i < 20000; i++)
            {
                int numberOfEvents = random.Next(1,1000);
                var generator = new RandomTimeStepGenerator(numberOfEvents);
                var stopwatch = new Stopwatch(); 
                stopwatch.Start();
                var steps = generator.MillisecondDeltas;
                Assert.AreEqual(numberOfEvents, steps.Count);
                var sum = generator.MillisecondDeltas.Sum();
                Assert.AreEqual(1000.0,sum,0.1);
                Assert.IsTrue(stopwatch.ElapsedMilliseconds<10);
            }

        }        
    }

    public class RandomTimeStepGenerator
    {
        private readonly int _numberOfEvents;
        const int timeResolution = 10000;

        public RandomTimeStepGenerator(int numberOfEvents)
        {
            _numberOfEvents = numberOfEvents;
        }

        public int NumberOfEvents
        {
            get { return _numberOfEvents; }
        }

        public List<double> MillisecondDeltas
        {
            get
            {
                var last=0;
                var result = new List<double>();
                var random = new Random();

                for (var i = 0; i < timeResolution && result.Count < _numberOfEvents; i++)
                {
                    var remainingEvents = _numberOfEvents - result.Count;
                    var remainingTime = timeResolution - i;
                    if(remainingEvents==1) // make sure the last event fires on the second
                    {
                        result.Add((timeResolution - last) / 10.0);
                        last = i;  
                    }
                    else if (remainingTime <= remainingEvents) // hurry up and fire you lazy !!! your going to run out of time 
                    {
                        result.Add(0.1);
                    }
                    else
                    {
                        double probability = remainingEvents / (double)remainingTime;
                        int next = random.Next(0,timeResolution);                        
                        if ((next*probability) > _numberOfEvents)
                        {
                            result.Add((i - last)/10.0);
                            last = i;
                        }
                    }                   
                }                 
                return result;
            }
        }
    }    
}
于 2012-08-30T19:50:33.240 に答える