0

これが本当に必要な理由: (下の edit3 の下の問題の簡単な説明)

想像してみましょう、私は twitter 用のアプリケーションを作成しようとしています。今または後で追加するのと同じ数のアカウントがあります (たとえば 10)。したがって、すべてのアカウントにはオプションがあります。

  1. RSS からつぶやきます。
  2. 人に従う;
  3. つぶやきをリツイートします。

1 つのアカウントについて話すとき、このタスクはすべてバックグラウンドで動作し、他のタスクと同期する必要があります。いずれかのアカウントで twite とフォローが一度に進行する (要求が多すぎる) ことはありません。

つまり、アカウントごとに一度に 1 つのタスクを作成する必要があります。これは、この瞬間にアクティブなアカウントごとに 1 つのスレッドを編成する必要があるということですか? しかし、すべてのアカウントに次に何をすべきかを伝える (キューをいっぱいにする) スレッドとは何でしょうか? あるアカウントはツイートしたいだけで、別のアカウントは単にリツイートして人々をフォローしています:

  1. (スレッド 1) アカウント A: RSS/ファイルから 1 日中ツイートしたい。
  2. (スレッド 2) アカウント B: 30 秒ごとにリツイートし、15 分ごとにフォローしたい。
  3. (スレッド 3) : 「アカウント A」のキューをいっぱいにする必要があります (make twite、make twite、make twite);
  4. (スレッド 4) : 「アカウント B」のキューをいっぱいにする必要があります (retwite を作成、retwite を作成 (15 分間、30 秒ごと) ... 人をフォロー、retwite を作成 ...);

この場合、アカウントごとに 1 つのスレッドが機能し (キューにあるもの -> 彼がそうした)、別のスレッドがキューをいっぱいにするだけで、機能の手動タスクを追加できます(現在、すべてのアカウントで、この twite のリツイートを作成します)。またはこの人に従ってください)、- すべてのキューに新しいタスクを追加し、すべてのスレッドをできるだけ早く実行します。

だから、私の目標(ターゲット)は、すべてのアカウントが自分の仕事をするべきアプリケーションを作成することであり、次に何をすべきかを伝えるオプションがあります(タスクの優先度を変更します)。

お聞きしたいのですが、キューについて、アカウントごとに約 2 スレッドでよろしいですか (おそらく、スレッドがアカウントを使用してタスクをマーク (区切り文字) として使用するグローバル キューである必要があります)、アドバイスをいただけないでしょうか。私のタスクについて何か。

編集: そして、そこにある最大の問題は、私が20のアカウントを作ったとしても、5つのスレッド(制限)しか持たないということです.グローバル)、その他は仕事をするためのものです-しかし、彼らは1つのアカウントのために仕事をすることができました(1つのスレッドはtwiteを作り、他のスレッドはユーザーをフォローし始めます)、そしてそれは私を窮地に陥らせます-理解できません、私はすべてをどのように整理すればよいですか?このスレッド。

Edit2:もう 1 つの問題: 実行時間。取得したタスクを実行する必要があるすべてのスレッド時間をチェックインする必要があります (次の 1 時間のタスクを計画します) (現在 23:55、このタスクは 23:59 に実行されます。次は何ですか? スキップしますか? スリープしますか?)。

時間を確認せず、実行する準備ができているタスクだけを追加するキューに入れるべきである場合、1 つのアカウント 1 つのタスクの問題が再び発生します (15 秒の遅延を追加する twite を追加する、20 秒の遅延を追加する retwite を追加する、人々をフォローする 60 秒の遅延) => 1 分ごとにキューに 3 つのタスクがありますが、1 つずつ実行する必要があります (100 個のアカウントがありますが、作業スレッドは 5 つしかありません (それらは互いに同期する必要があります。他のスレッドを実行するアカウント タスクは何ですか?)) .

Edit3 (簡単 にしましょう): わかりました、Twitter のことは忘れましょう。コンソールに何かを書き込む Windows デスクトップ アプリケーションを作成します (「ボブからこんにちは」、「ボブからこんにちは」、「ボブからさようなら」とします)。

プログラムに多くの名前 (Bob、Jack、John、Dave、Scott) を追加して、この名前がどのようなフレーズを記述する必要があるかを確認できます。

  1. Bob は 15 秒ごとに「Hello from Bob」のみを書き込みます。
  2. ジャックは 1 分ごとに「ジャックからこんにちは」と 5 秒ごとに「ジャックからこんにちは」と書きます。
  3. John write 'hello', 'hi', 'bye' + 'John' から 5 秒, 10 秒, 15 秒;
  4. など(20名とする);

私は願っています、それは大丈夫ですか?

したがって、今、アプリケーションを起動すると、この 20 個の名前のすべてがコンソールにフレーズを書き込む必要がありますが、時間内にすべてのスレッド (10 個のワーカー スレッドに制限させてください) に対して、名前ごとに 1 つのタスクのみをタスクにする必要があります。 (1 つのスレッドが「Hello from Jack」を書き込んでいる場合、このタスクを 01:42:00 に実行する必要がある場合でも、他のスレッドは「bye from Jack」を書き込んではいけません。Jack の 1 つのタスクが完了したときにのみ、他のスレッドが開始されます) .

(+) さらに、この名前の誰かに追加して、最も近いタスクが完了したらすぐに好きなフレーズを書くオプションが必要です。

私はあなたの助けを見つけることを願っています。

Edit4:そう:ここに画像の説明を入力

助けてくれてありがとう、今、私は何が必要で、アプリケーションを整理する方法を知っています。

最後に実現:

私は自分のコードを使用してタスクを実行しましたが、私にとっての解決策は最高のようです。

ここに画像の説明を入力

したがって、すべてのアカウントのキューを生成し、それらをアプリケーション タスクの 1 つのグローバル キューに入れると、それを TimerCallBack 関数に渡します。この関数は、実行する準備ができているタスクがキューにあるかどうかをチェックします (ただし、event_elements[0] のみをチェックします)。 ),- その場合は、isactive = true を設定し (個人キューの場合、前の終了前に次のイベントを実行しないようにします)、このキューを配置します。これは、反復後の最初のイベントごとに実行されます。

次に、コレクションとして Parallel.ForEach に渡します。

 Parallel.ForEach(readyToExec, new ParallelOptions { MaxDegreeOfParallelism = 5 }, eq =>
            {
                lock (eq)
                {
                    QueueElement exec_elem = eq.elements[0];
                    Console.WriteLine(exec_elem.timeToExecute + ": " + eq.account.Name + " " + exec_elem.EventType);
                    exec_elem.timeToExecute = DateTime.Now + exec_elem.timeDelay;
                    eq.timeLastExecute = DateTime.Now;
                    eq.elements.Sort();
                    eq.isactive = false;
                }
            });

ちなみに、実行するイベントを追加する前にチェックを追加しました:

        foreach (var equeue in queue)
        {
            if (!equeue.isactive && equeue.elements[0].readyToExecute)
            {
                if (DateTime.Now > equeue.timeLastExecute + TimeSpan.FromSeconds(5))
                {
                    equeue.isactive = true;
                    readyToExec.Add(equeue);
                }
                else
                {
                    equeue.elements[0].timeToExecute = DateTime.Now + TimeSpan.FromSeconds(5);
                }
            }
        }

1 つのアカウントのイベントを手動で遅延させて実行する (たとえば、00:00:00 に「Hello」と書き込んだ場合、次のイベントは 00:00:05 まで実行を開始できませんでした。この実行の時間が 00 であっても) :00:02)。

だから、それは私にとってとてもうまくいきます;)

4

1 に答える 1

0

私の最初の傾向は、優先キューとTimerを使用することです。これが私がそれを行う方法です。

最初にStopwatch、アプリケーション時間を維持するグローバルを作成します。夏時間やその他の時間の変更によってタイミングが台無しになることは望ましくありません。イベント間の待ち時間が長くなりすぎたり、イベントの発生が早すぎたりする可能性があるためです。だから、あなたが持っている場所:

static Stopwatch ApplicationTime = Stopwatch.StartNew();

Accountまた、Twitter アカウントに関する情報と、最も重要なこととして、そのアカウントの最後のアクションが実行された時刻を含むクラスもあります。

class Account
{
    // information about the account goes here

    public TimeSpan LastEventTime { get; set; }
}

そして、実行されるアクションを定義するイベント クラス。

class AccountEvent
{
    public Account acct { get; private set; }
    public TimeSpan dueTime { get; set; }
    public ActionType action { get; private set; } // follow, re-tweet, etc.

    // constructor, etc.
}

最後に、イベント時間によってキーが設定された優先キュー:

PriorityQueue<TimeSpan, AccountEvent> EventQueue = new Priority<TimeSpan, AccountEvent>();

残念ながら、.NET ランタイム ライブラリにはプライオリティ キュー クラスが組み込まれていません。グーグルで検索するといくつか出てきます。私が何年も前に書いたものはhttp://www.devsource.com/c/a/Languages/A-Priority-Queue-Implementation-in-C/にあります。http://mischel.com/pubs/priqueue.zipで最新のソースを入手できます。私の優先キューは同時ではないことに注意してください。その周りに同時実行レイヤーをラップする必要があります。あなたの目的のためにはlock、何らかの方法でキューにアクセスする前に単純なもので十分です。

さて、これらすべてのものを使用する方法:

アカウントを 30 分ごとにチェックするとします。AccountEvent次のように新しいものを作成します。

var ev = new AccountEvent(userAccount, ApplicationTime.Elapsed.AddMinutes(30), ActionType.Follow);
EventQueue.Enqueue(ev.dueTime, ev);

ここで、アイテムをキューから削除して処理するために何かが必要です。そこでタイマーの出番です:

Timer eventTimer = new System.Threading.Timer(QueueProc, null, TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(-1));

これにより、5 秒で起動するタイマーが作成されます。これは定期的なタイマーではなくワンショット タイマーであるため、タイマー ティックが重複することはありません。

ここで、タイマー ハンドラーは、処理が必要なイベントのキューをチェックします。

void QueueProc(object state)
{
    while (eventQueue.Count > 0 && eventQueue.Peek().dueTime < ApplicationTime.Elapsed)
    {
        AccountEvent ev = eventQueue.Dequeue();
        // process item. See comments below.
    }
    // reset the timer so that it will fire in five seconds
    eventTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(-1));
}

1 つのアカウントに複数のイベントを設定できるため、イベント処理は少し複雑ですが、それほど複雑ではありません。アカウントの最後のイベント時間に対して現在の時間を確認する必要があります。最後のイベントが近すぎる場合は、イベント時間に時間を追加し、新しい時間でキューに戻します。

アイテムを処理するには、非同期の Web 要求 (私が推奨する方法) を起動するか、 を使用するBackgroundWorkerか、 TPL を起動しTaskます。重要なのは、非同期リクエストが完了すると、完了コールバックが最後に行うことは、次のイベントの時間を更新し、そのイベントをキューに戻すことです。

ここでは詳細を省略しましたが、基本的な考え方は理解できると思います。少なくとも、始めるには十分です。

于 2012-11-17T01:28:04.537 に答える