5

私は、Webサービスをクライアントに公開する単純なサーバーに取り組んでいます。一部のリクエストは完了するまでに長い時間がかかる場合があり、論理的に複数のステップに分割されます。このような要求については、実行中に進捗状況を報告する必要があります。さらに、前のリクエストが完了する前に新しいリクエストが開始される可能性があり、両方を同時に実行する必要があります(システム固有の制限を除く)。

サーバーにTaskIdをクライアントに返してもらい、クライアントにTaskIdを使用してリクエストの進行状況を追跡させることを考えていました。これは良いアプローチだと思います。タスクの管理方法の問題が残っています。

TPLを使ったことがないので、この問題に取り組むのに良い方法だと思いました。実際、スレッドを手動で管理しなくても、複数のタスクを同時に実行できます。ContinueWithを使用すると、比較的簡単にマルチステップタスクを作成することもできます。

ただし、タスクの進行状況を追跡するための良い方法を思い付くことができません。私のリクエストが単一の「ステップ」で構成されている場合、そのステップは協力してその状態を報告する必要があることを認識しています。これは、現時点では避けたいものです。ただし、リクエストが複数のステップで構成されている場合は、現在実行中のステップを知り、それに応じて進捗状況を報告したいと思います。私が思いつくことができる唯一の方法は非常に面倒です:

Task<int> firstTask = new Task( () => { DoFirstStep(); return 3.14; } );
firstTask.
ContinueWith<int>( task => { UpdateProgress("50%"); return task.Result; } ).
ContinueWith<string>( task => { DoSecondStep(task.Result); return "blah"; }.
ContinueWith<string>( task => { UpdateProgress("100%"); return task.Result; } ).

また、UpdateProgressに既知の場所を更新させるのではなく、タスクに独自の進行状況を保存させたいので、これでも完全ではありません。さらに、新しいステップを追加するときに多くの場所を変更しなければならないという明らかな欠点があります(現在、進捗状況は50%、100%ではなく33%、66%、100%です)。

誰かが良い解決策を持っていますか?

ありがとう!

4

3 に答える 3

5

これは、タスク並列ライブラリが完全にサポートしているシナリオではありません。

進行状況の更新をキューにフィードし、別のタスクでそれらを読み取るアプローチを検討できます。

static void Main(string[] args)
{
    Example();
}

static BlockingCollection<Tuple<int, int, string>> _progressMessages = 
    new BlockingCollection<Tuple<int, int, string>>();

public static void Example()
{
    List<Task<int>> tasks = new List<Task<int>>();

    for (int i = 0; i < 10; i++)
        tasks.Add(Task.Factory.StartNew((object state) =>
            {
                int id = (int)state;
                DoFirstStep(id);
                _progressMessages.Add(new Tuple<int, int, string>(
                    id, 1, "10.0%"));
                DoSecondStep(id);
                _progressMessages.Add(new Tuple<int, int, string>(
                    id, 2, "50.0%"));

                // ...

                return 1;
            },
            (object)i
            ));

    Task logger = Task.Factory.StartNew(() =>
        {
            foreach (var m in _progressMessages.GetConsumingEnumerable())
                Console.WriteLine("Task {0}: Step {1}, progress {2}.",
                m.Item1, m.Item2, m.Item3);
        });


    List<Task> waitOn = new List<Task>(tasks.ToArray());
    waitOn.Add(logger);
    Task.WaitAll(waitOn.ToArray());
    Console.ReadLine();
}

private static void DoSecondStep(int id)
{
    Console.WriteLine("{0}: First step", id);
}

private static void DoFirstStep(int id)
{
    Console.WriteLine("{0}: Second step", id);
}

このサンプルには、キャンセル、エラー処理、またはタスクが長時間実行される可能性があるという要件の説明は含まれていません。長時間実行されるタスクは、スケジューラーに特別な要件を課します。これについての詳細はhttp://parallelpatterns.codeplex.com/にあり、本のドラフトをダウンロードして第3章を参照してください。

これは、このようなシナリオでタスク並列ライブラリを使用するための単なるアプローチです。ここでは、TPLが最善のアプローチではない可能性があります。

WebサービスがASP.NET(または同様のWebアプリケーションサーバー)内で実行されている場合は、Web要求を処理するのではなく、スレッドプールのスレッドを使用してタスクを実行することによる影響の可能性も考慮する必要があります。

タスク並列ライブラリは、ターミナルサーバーまたはWebアプリケーションでどのように拡張できますか?

于 2010-08-03T15:50:14.010 に答える
0

あなたが探している解決策がタスクAPIを含むとは思わない。または、少なくとも、直接ではありません。完了率の概念はサポートされていません。タスク/ContinueWith関数は、そのレベルでのみ使用可能なデータであるため、そのロジックに参加する必要があります(ContinueWithの最後の呼び出しのみが、完了率を知るための任意の位置にあります。それでも、アルゴリズムでこれを行うことは、一方のタスクがもう一方のタスクよりもはるかに長くかかるかどうかが確実にわからないため、せいぜい推測になります。これを行うために、おそらくタスクAPIを利用して独自のAPIを作成することをお勧めします。実際の仕事をするために。

于 2010-08-03T14:19:56.570 に答える
0

これは役立つかもしれません:http://blog.stephencleary.com/2010/06/reporting-progress-from-tasks.html。このソリューションでは、進行状況の報告に加えて、クロススレッド操作が無効な例外になることなくフォームコントロールを更新することもできます。

于 2013-10-25T06:02:15.907 に答える