1

我正在尝试实现一个简单的 Windows 8 C# XAML 应用程序,其中有两个调用来访问单个 Web 服务,一个来自项目以加载和显示数据,另一个用于显示通知。由于对同一个 Web 服务进行了两次调用,我希望如果已经对该服务进行了一次调用,则另一个调用应该等待并使用第一次调用的相同响应。

我怎样才能实现这种功能?我没有添加任何代码,因为我没有为此编写任何代码。我只是想先思考,然后再编码。

请让我知道我可以为这种项目结构获得一些帮助吗?

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()同时调用两次,也有很小的机会DownloadFromWebServiceAsync()会被调用两次。但由于这只是一个优化,我认为这不应该是一个问题。如果这对您来说是个问题,您需要用锁来保护进入该领域的通道。

于 2013-02-12T13:23:57.983 回答
0

由于我觉得这个问题需要进一步关注并且它的解决方案仍然可以优化,我决定发布另一种方法。OP 主要是关于利用以下 3 个需求范围的问题:应用程序内的用户体验、应用程序的内部需求Web 服务的加载多个请求。

  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 回答