1

私は単純な Windows 8 C# XAML アプリケーションを実装しようとしています。このアプリケーションでは、単一の Web サービスにアクセスするために 2 つの呼び出しが行われます。1 つはプロジェクトからデータをロードして表示するため、もう 1 つは通知を表示するためです。同じWebサービスに対して2つの呼び出しが行われたため、1つの呼び出しが既にサービスに対して行われている場合、他の呼び出しは待機して最初の呼び出しからの同じ応答を使用する必要があります。

この種の機能を実現するにはどうすればよいですか? このために書いたコードがないため、コードを追加していません。私は最初に考えようとしているだけで、それからコーディングします。

この種のプロジェクト構造について助けを得ることができることを教えてください。

4

3 に答える 3

1

これを行うには、Task現在ダウンロード中の をキャッシュし、キャッシュされた がある場合はダウンロードを再開しませんTask

private volatile Task<string> m_cachedWebServiceTask;

async Task<string> AccessWebServiceAsync()
{
    var task = m_cachedWebServiceTask;

    if (task == null)
        task = m_cachedWebServiceTask = DownloadFromWebServiceAsync();

    try
    {
        return await task;
    }
    finally
    {
        m_cachedWebServiceTask = null;
    }
}

このコードには競合状態があることに注意してください。AccessWebServiceAsync()同時に 2 回呼び出すDownloadFromWebServiceAsync()と、2 回も呼び出される可能性がわずかにあります。しかし、これは最適化にすぎないので、問題にはならないと思います。それが問題になる場合は、フィールドへのアクセスをロックで保護する必要があります。

于 2013-02-12T13:23:57.983 に答える
0

この問題にはさらに注意を払う必要があり、その解決策はまだ最適化できると感じたので、別のアプローチを投稿することにしました。OP は主に、アプリケーション内のユーザー エクスペリエンス、アプリケーションの内部要件、複数のリクエストによる Web サービスの読み込みという 3 つの範囲の要件を活用することに関する問題です。

  1. アプリケーションは、データをロードするために最初のリクエストを行う必要があります。
  2. 彼がそれを要求するとき、ユーザーは最新の更新で結果が得られることを期待しています。
  3. 一方で、非常に短い時間に Web サービスへの大規模な一連の呼び出しを開始することはありません。

したがって、この非常に短い時間で何が起こるかを管理することが、実際の問題の解決策になります。

クライアント側では、Service1Clientクラス:

public partial class Service1Client
{
    // default time buffer value
    int _timeBuffer = 100;

    // a time buffer used for returning the same response  
    public int TimeBuffer
    {
        get { return _timeBuffer; }
        set { _timeBuffer = value; }
    }
    // the start and end time of the web service request
    static DateTime _start, _end;

    // a volatile static variable to store the response in the buffering time
    volatile static string _response;

    // used for blocking other threads until the current thread finishes it's job
    object _locker = new object();

    public async Task<string> GetResponseData()
    {
        return await Task.Factory.StartNew<string>(() =>
        {
            lock (_locker)
            {
                if (DateTime.Now >= _end.AddMilliseconds(TimeBuffer))
                {
                    _start = DateTime.Now;
                    var async = GetDataAsync();
                    _response = async.Result;
                    _end = DateTime.Now;
                }
            }
            return _response;
        });
    }
}

テストに使用したコンソール アプリケーション:

class Program
{
    static void Main(string[] args)
    {
        while (true)
        {
            var client = new ServiceReference1.Service1Client();
            client.TimeBuffer = 150;
            Console.WriteLine(client.GetResponseData().Result);
            if (Console.ReadKey().Key == ConsoleKey.Enter)
                break;
        }
    }
}

注釈として、明確なサンプルの理由から、GetDateWCF サービスのメソッドの返される型を からDateTimeに変更することにしたことに注意してくださいstring

[ServiceContract]
public interface IService1
{
    [OperationContract]
    string GetData();
}

public class Service1 : IService1
{
    public string GetData()
    {
        System.Threading.Thread.Sleep(5000);
        return DateTime.Now.ToString();
    }
}
于 2013-02-13T09:22:53.760 に答える
-1

あなたのシナリオでは、実行可能なアイデアはサービス クラスを拡張することです。

IService1インターフェイス定義:

[ServiceContract]
public interface IService1
{
    [OperationContract]
    DateTime GetData();
}

Service1クラス定義:

public class Service1 : IService1
{
    public DateTime GetData()
    {
        System.Threading.Thread.Sleep(5000);
        return DateTime.Now;
    }
}

クライアント側で、Service1Clientクラス定義を拡張し、新しいメソッドを追加します。

public partial class Service1Client
{
    static bool _isOpen;

    static DateTime? _cachedResponse;
    object _locker = new object();

    public DateTime GetResponseData()
    {
        if (!_isOpen)
        {
            if (!_cachedResponse.HasValue)
            {
                lock (_locker)
                {
                    _isOpen = true;
                    _cachedResponse = GetData();
                    _isOpen = false;
                }
                return _cachedResponse.Value;
            }
            else
            {
                Task.Factory.StartNew<DateTime>(() =>
                {
                    lock (_locker)
                    {
                        _isOpen = true;
                        _cachedResponse = GetData();
                        _isOpen = false;
                    }
                    return _cachedResponse.Value;
                });
            }
        }
        return _cachedResponse.Value;
    }
}

試して:

class Program
{
    static void Main(string[] args)
    {
        while (true)
        {
            var client = new ServiceReference1.Service1Client();
            Console.WriteLine(client.GetResponseData());
            if (Console.ReadKey().Key == ConsoleKey.Enter)
                break;
        }
    }
}
于 2013-02-12T14:08:45.730 に答える